作者:HelloGitHub-Prodesire
一、介绍
本篇文章是《聊聊 Python 的单元测试框架》的第三篇,前两篇分别介绍了标准库 unittest 和第三方单元测试框架 nose。作为本系列的最后一篇,压轴出场的是Python 世界中最火的第三方单元测试框架:pytest。
pytest 项目地址:https://github.com/pytest-dev/pytest
它有如下主要特性:
- assert[1] 断言失败时输出详细信息(再也不用去记忆 self.assert* 名称了)
- 自动发现[2] 测试模块和函数
- 模块化夹具[3] 用以管理各类测试资源
- 对 unittest 完全兼容,对 nose 基本兼容[4]
- 非常丰富的插件体系,有超过 315 款第三方插件[5],社区繁荣
和前面介绍 unittest 和 nose 一样,我们将从如下几个方面介绍 pytest 的特性。
二、用例编写
同 nose 一样,pytest 支持函数、测试类形式的测试用例。最大的不同点是,你可以尽情地使用 assert 语句进行断言,丝毫不用担心它会在 nose 或 unittest 中产生的缺失详细上下文信息的问题。
比如下面的测试示例中,故意使得 test_upper 中断言不通过:
import pytestdef test_upper(): assert 'foo'.upper() == 'FOO1'class TestClass: def test_one(self): x = "this" assert "h" in x def test_two(self): x = "hello" with pytest.raises(TypeError): x + []
而当使用 pytest 去执行用例时,它会输出详细的(且是多种颜色)上下文信息:
=================================== test session starts ===================================platform darwin -- Python 3.7.1, pytest-4.0.1, py-1.7.0, pluggy-0.8.0rootdir: /Users/prodesire/projects/tests, inifile:plugins: cov-2.6.0collected 3 itemstest.py F.. [100%]======================================== FAILURES =========================================_______________________________________ test_upper ________________________________________ def test_upper():> assert 'foo'.upper() == 'FOO1'E AssertionError: assert 'FOO' == 'FOO1'E - FOOE + FOO1E ? +test.py:4: AssertionError=========================== 1 failed, 2 passed in 0.08 seconds ============================
不难看到,pytest 既输出了测试代码上下文,也输出了被测变量值的信息。相比于 nose 和 unittest,pytest 允许用户使用更简单的方式编写测试用例,又能得到一个更丰富和友好的测试结果。
三、用例发现和执行
unittest 和 nose 所支持的用例发现和执行能力,pytest 均支持。pytest 支持用例自动(递归)发现:
- 默认发现当前目录下所有符合 test_*.py 或 *_test.py 的测试用例文件中,以 test 开头的测试函数或以 Test 开头的测试类中的以 test 开头的测试方法
- 使用 pytest 命令
- 同 nose2 的理念一样,通过在配置文件[6]中指定特定参数,可配置用例文件、类和函数的名称模式(模糊匹配)
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
四、测试夹具(Fixtures)
pytest 的测试夹具[7]和 unittest、nose、nose2的风格迥异,它不但能实现 setUp 和 tearDown这种测试前置和清理逻辑,还其他非常多强大的功能。
4.1 声明和使用
pytest 中的测试夹具更像是测试资源,你只需定义一个夹具,然后就可以在用例中直接使用它。得益于 pytest 的依赖注入机制,你无需通过from xx import xx的形式显示导入,只需要在测试函数的参数中指定同名参数即可,比如:
import pytest@pytest.fixturedef smtp_connection(): import smtplib return smtplib.SMTP("smtp.gmail.com