Pytest
pytest 是 Python 中非常流行的测试框架,它有许多强大且灵活的功能,适用于各种测试需求,同时还有强大的插件支持。
常用插件:
安装
命名规范
- 测试文件、测试方法:以
test_ 开头。
- 测试类名:以
TestXxx 开头。
测试结果
pytest 每个测试方法都是通过 assert 断言来判断测试执行结果的,如果断言不通过则测试结果为 Failed,如果断言通过则为 PASSED,如果中途出现其他报错信息,则为 Warning,也属于 Failed。
def test_assert_passed(): assert 1 == 1
def test_assert_field(): assert 1 == 2
def test_assert_warning(): assert 1 / 0 == 1
|
常用mark
参数化测试
可以通过 pytest.mark.parametrize 装饰器提供多个测试数据,避免重复编写类似的测试。
@pytest.mark.parametrize("num1, num2", [(1,2,3),(3,4,5)]) def test_addition(num1, num2): assert num1 + num2 == expected
|
跳过测试
@pytest.mark.skip(reason="暂时跳过") def test_skip(): assert 1 + 1 == 2
@pytest.mark.skipif(sys.platform == 'win32', reason="Windows 下不支持") def test_skip_if(): assert 1 + 1 == 2
|
预期失败标记
@pytest.mark.xfail(reason="已知 Bug 待修复") def test_example(): assert 1 + 1 == 3
|
添加标记
pytest 允许你为测试函数添加自定义标记,以便后续指定是否允许。
@pytest.mark.markName def test_long_running(): assert True
|
测试夹具
pytest 的夹具用于创建测试前的环境或数据。夹具可以通过装饰器定义,并且可以在多个测试中复用。
@pytest.fixture(scope="module", autouse=True) def setup_data(): print("\n[Setup] 准备测试环境") yield {'username': 'testuser', 'password': 'password123'} print("\n[Teardown] 清理测试环境")
def test_login(setup_data): assert setup_data['username'] == 'testuser'
|
pytest 允许为夹具指定不同的作用域:
-
scope="function"(默认值):每个测试用例都会调用一次夹具。
-
scope="module":每个模块只调用一次夹具。
-
scope="class":每个测试类调用一次夹具。
-
scope="session":整个测试会话只调用一次夹具。
pytest 还支持自动调用夹具,使用 autouse=True 让夹具自动应用到每个测试中。
pytest 通过yield关键字,可以在fixture中实现资源的创建和销毁。
预期异常
def test_zero_division(): with pytest.raises(ZeroDivisionError): 1 / 0
|
日志记录
import logging
def test_logging(): logging.basicConfig(level=logging.DEBUG) logging.debug("This is a debug message") assert True
|
捕获输出
def test_print(capsys): print("Hello, pytest!") captured = capsys.readouterr() assert captured.out == "Hello, pytest!"
|
钩子函数
def pytest_configure(config): print("pytest 配置已设置")
def pytest_runtest_protocol(item, nextitem): print(f"运行测试: {item.name}") return None
|
构建/销毁
-
xxx_method:在每个测试方法运行之前/后执行一次。
-
xxx_class:在测试类中的所有测试方法运行之前/后执行一次。
class TestExample:
@classmethod def setup_class(cls): print(f"Setup class: {cls.__name__}") @classmethod def teardown_class(cls): print(f"Tear down class: {cls.__name__}") def setup_method(self, method): print(f"Setup method: {method.__name__}") def teardown_method(self, method): print(f"Tear down method: {method.__name__}")
|
运行
你可以通过指定文件、类、方法来运行特定的测试。
格式:文件名::类名::方法名
- 基本运行
pytest test_example.py::TestMathOperations::test_addition
|
- 生成 HTML 的测试报告。
pytest --html=./report.html
|
- 指定标记运行。
pytest -m markName
pytest -m "not markName"
pytest -m "markName1 and markName2"
pytest -m "markName1 or markName2"
|
- python代码运行。
pytest.main(['-vs', '-m markName', '--html=./report.html', './test_cases'])
|
- 使用
allure-pytest 生成JSON数据,并生成 allure 的测试报告。
pytest --alluredir=./json_report
allure generate ./json_report -o ./html_report --clean
|
- 使用
pytest-xdist 并行测试。
使用 auto 会使用全部CPU来执行。
- 使用
pytest-cov 生成覆盖率。
pytest --cov=moduleName --cov-report=html
|
会生成htmlcov报告目录及.coverage数据文件。
- 使用
pytest-timer 生成测试时间。
pytest --timer
pytest --timer-top-n N
pytest --timer-filter ok|warning|error
|
- 使用
pytest-forked 进行隔离测试。