各位观众,早上好/下午好/晚上好! 欢迎来到今天的“Python高级技术之pytest
的markers
”讲座。今天我们来聊聊pytest
中一个非常实用,但又经常被忽视的功能——markers
。用好了它,你的测试用例管理和执行效率绝对能上一个台阶。
什么是markers
?简单粗暴的定义
markers
,你可以把它理解为测试用例的“标签”或者“分组标识”。就像给文件打标签一样,你可以给你的测试用例打上各种各样的标签,比如“性能测试”、“数据库测试”、“UI测试”、“冒烟测试”等等。
markers
有什么用?
有了这些标签,你就可以:
- 分组执行测试用例:只运行打着特定标签的测试用例。
- 排除特定测试用例:跳过打着某些标签的测试用例。
- 为测试用例添加元数据:比如,标记某个测试用例需要特定的环境或者参数。
- 生成测试报告:根据标签对测试结果进行分类和统计。
总而言之,markers
能让你更灵活、更有条理地管理和执行你的测试用例。
markers
的基本用法:上手非常容易
-
注册
markers
(可选但推荐)虽然
pytest
允许你直接使用未注册的markers
,但强烈建议你先在pytest.ini
或pyproject.toml
文件中注册你的markers
。这样做的好处是:pytest
会检查你的markers
是否拼写正确。- 你可以为
markers
添加描述信息,方便其他人理解它的作用。
在
pytest.ini
中注册markers
的格式如下:[pytest] markers = smoke: 冒烟测试 regression: 回归测试 database: 数据库测试 ui: UI测试 performance: 性能测试
如果你喜欢用
pyproject.toml
,格式如下:[tool.pytest.ini_options] markers = [ "smoke: 冒烟测试", "regression: 回归测试", "database: 数据库测试", "ui: UI测试", "performance: 性能测试", ]
-
在测试用例上添加
markers
使用
@pytest.mark.<marker_name>
装饰器来给测试用例添加markers
。例如:import pytest @pytest.mark.smoke def test_login_success(): """测试登录成功场景""" assert True @pytest.mark.regression def test_add_to_cart(): """测试添加商品到购物车""" assert True @pytest.mark.database def test_query_user_info(): """测试查询用户信息""" assert True
-
运行指定
markers
的测试用例使用
-m
选项来指定要运行的markers
。-
运行所有带有
smoke
标记的测试用例:pytest -m smoke
-
运行所有带有
smoke
或regression
标记的测试用例:pytest -m "smoke or regression"
-
运行所有带有
smoke
但没有database
标记的测试用例:pytest -m "smoke and not database"
-
运行所有没有标记的测试用例
pytest -m "not <any registered marker>" # 例如: pytest -m "not smoke"
注意: 表达式里面的
and
,or
,not
是大小写敏感的。 -
markers
的高级用法:让你的测试更强大
-
带参数的
markers
markers
不仅可以作为一个简单的标签,还可以携带参数。这在需要为测试用例提供不同的配置或者数据时非常有用。import pytest @pytest.mark.parametrize("browser", ["chrome", "firefox", "safari"]) def test_open_browser(browser): """测试打开不同浏览器""" print(f"Opening browser: {browser}") assert True @pytest.mark.timeout(seconds=5) def test_long_running_task(): """测试长时间运行的任务""" import time time.sleep(3) # 模拟耗时操作 assert True
在这个例子中,
@pytest.mark.parametrize
和@pytest.mark.timeout
都是带参数的markers
。parametrize
用于参数化测试,timeout
用于设置测试用例的超时时间。 -
自定义
markers
除了
pytest
内置的markers
(比如parametrize
,xfail
,skip
),你还可以自定义markers
来实现更复杂的功能。比如,你可以创建一个
needs_db
的marker
,用于标记需要数据库连接的测试用例。import pytest @pytest.fixture(scope="session") def db_connection(): """创建数据库连接""" print("Connecting to database...") conn = ... # 这里替换为你的数据库连接代码 yield conn print("Closing database connection...") conn.close() def pytest_configure(config): config.addinivalue_line( "markers", "needs_db: Mark test as needing a database connection." ) @pytest.mark.needs_db def test_query_data(db_connection): """测试查询数据""" data = db_connection.query("SELECT * FROM users") assert len(data) > 0
在这个例子中,
pytest_configure
是一个钩子函数,用于在pytest
启动时进行配置。我们使用config.addinivalue_line
函数注册了needs_db
这个marker
。db_connection
是一个fixture,用于提供数据库连接。 在test_query_data
测试用例中,我们使用@pytest.mark.needs_db
标记该用例需要数据库连接,并且使用了db_connection
fixture。注意事项:
pytest_configure
函数必须在conftest.py
文件中定义,或者在插件中定义。conftest.py
文件应该放在你的测试根目录下,或者任何包含测试用例的子目录下。
-
markers
和fixture
的结合markers
和fixture
结合使用,可以实现更强大的测试控制。例如,你可以根据marker
来选择不同的fixture
。import pytest @pytest.fixture def slow_operation(): """模拟一个耗时操作""" import time time.sleep(2) return "Slow result" @pytest.fixture def fast_operation(): """模拟一个快速操作""" return "Fast result" def pytest_runtest_setup(item): """根据marker选择不同的fixture""" if "slow" in item.keywords: item.fixturenames.append("slow_operation") else: item.fixturenames.append("fast_operation") @pytest.mark.slow def test_slow_task(slow_operation): """测试耗时任务""" print(f"Result: {slow_operation}") assert True def test_fast_task(fast_operation): """测试快速任务""" print(f"Result: {fast_operation}") assert True
在这个例子中,
pytest_runtest_setup
是一个钩子函数,用于在每个测试用例运行之前进行设置。我们检查测试用例是否带有slow
这个marker
,如果有,则将slow_operation
这个fixture添加到测试用例的fixture列表中;否则,添加fast_operation
这个fixture。解释:
item.keywords
:包含了测试用例的所有markers
和名称。item.fixturenames
:是一个列表,包含了测试用例需要使用的所有fixture的名称。pytest_runtest_setup
:在每个测试用例执行前都会被调用。
-
使用
mark
装饰器堆叠多个markers
有时,你可能需要为一个测试用例添加多个
markers
。可以使用mark
装饰器堆叠多个markers
。import pytest @pytest.mark.smoke @pytest.mark.regression def test_important_feature(): """测试重要功能""" assert True
这个测试用例同时被打上了
smoke
和regression
两个markers
。 -
动态添加
markers
有时候,你可能需要在运行时根据某些条件动态地添加
markers
。import pytest def pytest_collection_modifyitems(config, items): """动态添加markers""" for item in items: if item.name.startswith("test_api_"): item.add_marker(pytest.mark.api) if item.getparent(pytest.Module).name == "test_slow.py": item.add_marker(pytest.mark.slow) def test_api_get_data(): """测试API获取数据""" assert True def test_normal_flow(): """测试正常流程""" assert True
在这个例子中,
pytest_collection_modifyitems
是一个钩子函数,用于在测试用例收集完成后修改测试用例列表。我们遍历所有测试用例,如果测试用例的名称以test_api_
开头,则添加api
这个marker
。如果测试用例位于test_slow.py
文件中,则添加slow
这个marker
。解释:
pytest_collection_modifyitems
:在测试用例收集完成后被调用。items
:是一个列表,包含了所有收集到的测试用例。item.name
:是测试用例的名称。item.getparent(pytest.Module).name
:是测试用例所在的模块的名称。item.add_marker()
:用于向测试用例添加marker
。
markers
的常见使用场景:让测试更有针对性
使用场景 | 描述 | 示例 |
---|---|---|
冒烟测试 | 快速验证核心功能是否正常,用于快速排查问题。 | @pytest.mark.smoke |
回归测试 | 验证修改后的代码是否引入新的问题,确保原有功能正常运行。 | @pytest.mark.regression |
UI测试 | 测试用户界面是否符合设计规范,用户交互是否流畅。 | @pytest.mark.ui |
数据库测试 | 测试数据库连接是否正常,数据读写是否正确。 | @pytest.mark.database |
性能测试 | 测试系统的性能指标,比如响应时间、吞吐量等。 | @pytest.mark.performance |
兼容性测试 | 测试系统在不同平台、浏览器、设备上的兼容性。 | @pytest.mark.platform("windows") , @pytest.mark.browser("chrome") |
安全性测试 | 测试系统的安全性,比如是否存在漏洞、是否容易被攻击。 | @pytest.mark.security |
需要特定环境 | 标记测试用例需要在特定的环境下运行,比如需要特定的操作系统或者硬件设备。 | @pytest.mark.env("staging") , @pytest.mark.hardware("GPU") |
测试不同的数据 | 使用parametrize marker参数化测试,用不同的数据运行相同的测试逻辑。 |
@pytest.mark.parametrize("username, password", [("user1", "pass1"), ("user2", "pass2")]) |
标记预期失败 | 使用xfail marker标记预期失败的测试用例,如果测试用例确实失败了,pytest不会将其视为错误。 |
@pytest.mark.xfail(reason="Known issue with feature X") |
跳过测试 | 使用skip marker跳过某些测试用例,比如某些功能尚未实现或者不适用于当前环境。 |
@pytest.mark.skip(reason="Feature not yet implemented") |
标记耗时测试 | 标记耗时较长的测试,可以单独运行或者跳过。 | @pytest.mark.slow |
依赖测试 | 使用第三方插件 (例如 pytest-dependency ) 标记测试用例之间的依赖关系,确保某些测试用例在其他测试用例通过后才能运行。 |
需要安装 pytest-dependency 插件,然后可以使用 @pytest.mark.dependency(depends=["test_login"]) |
最佳实践:让markers
发挥最大价值
- 保持
markers
的命名规范:使用清晰、简洁、易懂的名称,避免使用模糊不清的缩写或者术语。 - 为
markers
添加描述信息:在pytest.ini
或pyproject.toml
文件中为markers
添加详细的描述信息,方便其他人理解它的作用。 - 避免过度使用
markers
:不要为每个测试用例都添加markers
,只为那些需要分组或者筛选的测试用例添加。 - 定期清理不再使用的
markers
:随着项目的演进,某些markers
可能不再使用,应该及时清理,避免造成混乱。 - 利用
markers
生成更丰富的测试报告:可以使用pytest-html
或者其他报告生成插件,根据markers
对测试结果进行分类和统计,生成更易于理解的测试报告。 - 使用
markers
控制测试环境:可以结合fixture
和markers
,根据marker
来选择不同的测试环境配置,比如使用不同的数据库或者不同的API endpoint。
markers
的局限性
虽然markers
非常强大,但也存在一些局限性:
- 无法实现复杂的依赖关系:
markers
只能简单地分组和筛选测试用例,无法表达复杂的依赖关系,比如A测试必须在B测试之后运行。对于复杂的依赖关系,可以考虑使用pytest-dependency
插件。 - 容易被滥用:如果过度使用
markers
,可能会导致测试用例管理混乱,难以维护。 - 需要手动维护:
markers
需要手动添加和维护,如果忘记添加或者添加错误,可能会导致测试结果不准确。
总结:markers
是pytest
的瑞士军刀
pytest
的markers
是一个非常实用且强大的功能,它可以让你更灵活、更有条理地管理和执行你的测试用例。掌握markers
的用法,可以极大地提高你的测试效率和质量。把它想象成pytest
的瑞士军刀,用对了地方,绝对能让你事半功倍。
希望今天的讲座对你有所帮助! 祝大家测试愉快!