Python单元测试
1. PyTest安装:
1 | >>> pip install pytest |
2. 基础用法
定义一个函数如下:
1 | def get_sum(a, b): |
为了验证其功能,我们可以编写单测用例如下:
1 | import pytest |
运行用例:
1 | >>> python -m pytest -v test_tmp.py -s |
2.1 命令行参数
可以通过pytest -help 查看支持的参数。以下是一些常用的参数:
-v: 输出更详细的用例执行信息, 不使用 -v 参数,运行时不会显示运行的具体测试用例名称;-s: 显示print内容 在运行测试用例时,为了调试或打印一些内容,我们会在代码中加一些print内容,但是这些内容默认不会显示出来。如果带上-s,就可以显示了。-x: 出现一条测试用例失败就退出测试。-m: 用表达式指定多个标记名。 pytest 提供了一个装饰器 @pytest.mark.xxx,用于标记测试并分组,以便你快速选中并运行,各个分组直接用 and、or 来分割。
2.2 选择执行的测试用例(静态)
按文件夹执行
1 | 执行指定文件夹及子文件夹下的所有测试用例 |
按文件执行
1 | 运行test_tmp.py下的所有的测试用例 |
按测试类执行
1 | pytest 文件名.py::测试类 |
按测试方法执行
1 | pytest 文件名.py::测试类::测试方法 |
选择执行的测试用例(动态)
如要使用动态指定测试用例的方式,首先需要给测试用例打标签(mark),比如在 class、method 上加上如下装饰器:
1 |
在运行时,可以根据标签来动态的选择哪些用例需要执行
1 | 同时选中带有这两个标签的所有测试用例运行 |
除此之外还提供了一种通过模糊匹配的方式选择测试用例的方式:
1 | -k 参数是按照文件名、类名、方法名、标签名来模糊匹配的 |
3. mock使用
pytest自带的unittest框架中默认集成了mock库,PyTest的mock支持是通过插件实现的。相对来讲PyTest使用起来更简单(PyTest的mocker是对原生mock的一个兼容,原生mock支持的功能mocker基本都可以支持)
3.1 基础用法
1 | def get_sum(a, b): |
运行后可以发现,原本get_sum的print内容并没有被打印出来,我们通过mocker.patch方法屏蔽掉了原函数,转而直接返回我们指定的返回结果
3.2 其他用法
mocker.patch的函数定义
1 | unittest.mock.patch(target, new=DEFAULT, spec=None, create=False, spec_set=None, autospec=None, new_callable=None, **kwargs) |
常用参数含义:
target: 模拟对象的路径,参数必须是一个str,格式为’package.module.ClassName’,注意这里的格式一定要写对。如果对象和mock函数在同一个文件中,路径要加文件名return_value: 模拟函数返回的结果side_effect: 调用mock时的返回值,可以是函数,异常类,可迭代对象。当设置了该方法时,如果该方法返回值是DEFAULT,那么返回return_value的值,如果不是,则返回该方法的值。 return_value 和 side_effect 同时存在,side_effect会返回。(如果 side_effect 是异常类或实例时,调用模拟程序时将引发异常。如果 side_effect 是可迭代对象,则每次调用 mock 都将返回可迭代对象的下一个值。如果设置为函数时其具体表现会替换被mock函数)
4. MagicMock
在mock的过程中,有时我们需要构造相对复杂的返回值,比如对db操作函数的mock,返回值往往是一个对象。这时候常规做法我们就需要定义一个类,并且将其实例化。
这种做法较为麻烦,且不够灵活。Python提供了一个MagicMock方法,我们可以较为方便的构造我们想要的数据类型。
1 | class TestTmpFunction(object): |
在工程实践中,我们一般对MagicMock在进行一次封装
1 | def factory(attrs=None, **kwargs): |
5. 数据驱动
某些时候,我们希望我们的单测可以覆盖多种逻辑分支,这时为每一种case都单独写一个测试明显也是不现实的。PyTest为单测提供了参数化功能,也就是数据驱动
1 | class TestTmpFunction(object): |
6. 代码覆盖率
PyTest提供了pytest-cov插件来实现代码覆盖率的统计.
6.1 基础用法
1 | >>> pytest --cov --cov-report=xml |
6.2 生成差异报表
1 | >>> diff-cover coverage.xml --compare-branch=origin/master --html-report report.html --fail-under=80 |
Python单元测试
https://smartmalphite.github.io/2022/08/10/PythonNote/PythonUnitTest/