- 测试夹具(test fixture)
- pytest 的测试夹具
- 作用域
- 使用方式
- 1. 作为测试函数的参数传入 (接上个示例)
- 2. 放入
conftest.py
共享夹具,这样也省去了导入工作 - 3. 共享测试数据(同时,pytest 会缓存这些数据)
- 4. 作为初始化和销毁。即 AOP 中的Before、After
- 5. 将测试函数的数据传递给夹具
- 模块化实现:从夹具函数使用夹具">6. 模块化实现:从夹具函数使用夹具
- 7. 通过夹具自动将测试函数分组
- 夹具对类和模块的支持 (使用
@pytest.mark.usefixtures
)">8. 夹具对类和模块的支持 (使用@pytest.mark.usefixtures
)
- 对 xunit 形式的夹具支持
- Pytest 测试文件发现
- 测试跳过与指定失败
- pytest 的测试夹具
- 命令行
测试夹具(test fixture)
测试夹具:适用于所有测试代码的初始化和清理的(代码)方法
在 Pytest 中也进行了阐述:
Software test fixtures initialize test functions. They provide a fixed baseline so that tests execute reliably and produce consistent, repeatable, results. Initialization may setup services, state, or other operating environments. These are accessed by test functions through arguments; for each fixture used by a test function there is typically a parameter (named after the fixture) in the test function’s definition.
pytest 的测试夹具
Pytest only caches one instance of a fixture at a time, which means that when using a parametrized fixture, pytest may invoke a fixture more than once in the given scope. https://docs.pytest.org/en/stable/how-to/index.html https://docs.pytest.org/en/stable/reference/fixtures.html
作用域
Pytest 的测试夹具使用是装饰器 @pytest.fixture(scope=)
,与作用域对应。即可以是
session
从 Pytest 测试执行开始,直到测试结束后销毁package
当前包下的测试结束后销毁module
当前模块下的测试结束后销毁class
当前类下的测试结束后销毁function
当前函数/方法结束后销毁
例如:
# content of conftest.py
import pytest
import smtplib
@pytest.fixture(scope="module")
def smtp_connection():
return smtplib.SMTP("smtp.gmail.com", 587, timeout=5)
使用方式
用途诸如参数传递、共享数据和参数化的支持等,比如测试用例执行总会需要”登录”状态,数据库连接的建立等等。因为较为庞大与灵活,这里只提及几个常见且重要的能力,具体的更多的技巧方式见 官方 fixture 的使用。 需要注意的是,fixture 实例化是有顺序/优先级的,且
@pytest.mark.usefixtures
与@pytest.fixture
叠用有不可预测性
1. 作为测试函数的参数传入 (接上个示例)
def test_ehlo(smtp_connection): #夹具smtp_connection作为参数让函数 test_ehlo 接收。其实这就是 DI 实现
response, msg = smtp_connection.ehlo() #调用夹具实例smtp_connection
assert response == 250
2. 放入 conftest.py
共享夹具,这样也省去了导入工作
it automatically gets discovered by pytest. The discovery of fixture functions starts at test classes, then test modules, then
conftest.py
files and finally builtin and third party plugins
引用该共享夹具见下文第8项 夹具对类和模块的支持 (使用
@pytest.mark.usefixtures
)
3. 共享测试数据(同时,pytest 会缓存这些数据)
4. 作为初始化和销毁。即 AOP 中的Before、After
5. 将测试函数的数据传递给夹具
_@pytest.mark.fixt_data()_
个人建议:对于此类诉求,不借助夹具参数反转能力,而是使用函数的参数。 那么此类能力的场景适用于什么时候?——数据驱动的模型
6. 模块化实现:从夹具函数使用夹具
7. 通过夹具自动将测试函数分组
- 可以通过 pytest.mark 的能力
- 可以通过作用域
8. 夹具对类和模块的支持 (使用@pytest.mark.usefixtures
)
对 xunit 形式的夹具支持
While these setup/teardown methods are simple and familiar to those coming from a
unittest
ornose
background, you may also consider using pytest’s more powerful fixture mechanism which leverages the concept of dependency injection, allowing for a more modular and more scalable approach for managing test state, especially for larger projects and for functional testing. You can mix both fixture mechanisms in the same file but test methods ofunittest.TestCase
subclasses cannot receive fixture arguments.
Pytest 测试文件发现
测试文件发现将默认递归(recurse)查询指定规则的py文件。
- 默认的测试文件发现规则
- 包名
test_*.py
或*_test.py
以及导入的符合规则的包 - 类名称为
Test
前缀。且类不能有__init__()
构造方法 - 函数名为
test_
前缀。
——对于这类通用默认模式,最好不要做个性化,就像 Python 的 _self_
- py包中
__init__.py
对测试文件发现的作用与影响
测试跳过与指定失败
命令行
pytest 命令执默认从用户当前所在目录开始执行与测试发现规则匹配的文件,指定了执行地址和文件等规则除外。具体支持的命令见帮助文档
常用的一些参数
-v
输出执行的详细信息(可以用于 debug)--pdb
调试信息管理器。更多调试的方式与信息见 PDB-s
异常捕获的快捷模式。比如显示 print 方法,异常抛出时的简要描述等-m
执行指定的测试文件--html=path
生成 html 格式测试报告到指定的path路径--full-trace
详细的链路追踪--runxfail
忽略 xfail 标记,执行测试并生成报告-x
测试失败即退出,不再继续执行--lf
重执行最近一次失败的用例,没有失败则执行全部--ff
执行所有测试用例,失败的优先--maxfail=num
限定次数失败后停止测试--ignore=path
不执行指定的文件--alluredir=DIR
生成 allure 报告到指定路径
命令执行示例:
$ python3 -v -m pytest website_run_test.py --html=../../reports/WebsiteTestReport.html --self-contained-html
Pycharm 配置示例:
一些其他的特性调用补充
如指定执行的类型、范围,或是函数、类等
markers 的使用
def pytest_configure(config):
"""
通过 @pytest.mark 调用。用于测试用例管理
e.g @pyetest.mark.bvt 即定义某用例级别为构建认证测试(金丝雀测试)
"""
marker_list = ["bvt", "smoke"]
for markers in marker_list:
config.addinivalue_line("markers", markers)
@pytest.mark.bvt
@allure.severity(allure.severity_level.BLOCKER)
def test_login(self, browser):
"""正常登录"""
login(browser)