【Python】单元测试实践内部指南

2021-10-28 15:15:04 浏览数 (1)

对于单元测试,我们的总的原则是:

单元测试应该写,因为这样才能保证程序的质量和养成良好的习惯,但是又不能将单元测试搞得太复杂,花太多的精力在这上面,那就本末倒置了

pytest的简单使用


单元测试工具选用pytest(这个工具和go test有点类似),简单的使用:

代码语言:javascript复制
# 文件: example.py
def func(i: int) -> int:
    return i * 2

# 文件: example_test.py
from .example import func
def test_func():
    assert func(10) == 20
    assert func(20) == 30
代码语言:javascript复制

在相同的目录下执行命令pytest,该命令会自动找到*_test.py的文件(注意:当前目录需要文件__init__.py)执行测试用例。显然这个单元测试是不通过的,报错信息如下:

代码语言:javascript复制
    def test_func():
        assert func(10) == 20
>       assert func(20) == 30
E       assert 40 == 30
E           where 40 = func(20)

doctest_example_test.py:7: AssertionError
代码语言:javascript复制

pytest可以递归自动发现测试文件,在使用过程中,也支持执行指定用例:

  • 指定测试文件路径 pytest /path/to/test/file.py
  • 指定测试类 pytest /path/to/test/file.py:TestCase
  • 指定测试方法 pytest another.test::TestClass::test_method
  • 指定测试函数 pytest /path/to/test/file.py:test_function

关于单元测试的几个规范


关于单元测试,我们定义几个使用规范:

  1. 我们写的函数或者类等,要时刻保持可测试的状态(或者说叫可观测的状态)。
  2. 测试用例文件名要对应,例如文件名为filename.py,则对应的测试用例文件名为filename_test.py。
  3. 测试函数名要对应,例如业务函数名为func_name,则对应的测试函数名为test_func_name。
  4. 测试类名要对应,例如原类名为ClassName,则对应测试类名为TestClassName。
  5. 建议测试用例文件和功能文件放在相同目录下,方便查找,通常不需要一个单独的测试目录。

使用参数化测试来优化测试用例


代码语言:javascript复制
在实际使用中,我们应该优先考虑使用参数化测试:
# 前面那个测试用例其实应该优化成这样:
import pytest

# 这里数据的部分完全可以定义成一个变量
# 这样就不必重复写很多个assert语句了
@pytest.mark.parametrize("params, expected", [([10], 20), ([20], 30)])
def test_func2(params, expected):
    assert func(*params) == expected
同样,执行之后,测试也是不通过的:
代码语言:javascript复制
    @pytest.mark.parametrize("params, expected", [([10], 20), ([20], 30)])
    def test_func2(params, expected):
>       assert func(*params) == expected
E       assert 40 == 30
E           where 40 = func(*[20])

doctest_example_test.py:18: AssertionError
代码语言:javascript复制

如果把这个parametrize函数进行封装的话,应该可以做到更加简单,例如单元测试只要描述输入输出即可:

代码语言:javascript复制
# 只要专注于编写测试用例即可
test_data = [
    (func, [10], {}, 20), 
    (func, [20], {}, 30)
]

@pytest.mark.parametrize("action, args, kwargs, expected", test_data)
def test_all_func(action, args, kwargs, expected):
    assert action(*args, **kwargs) == expected
这样,只要专注写好测试用例即可。

20210626

0 人点赞