pytest 是一个非常灵活且强大的测试框架,它支持简单的单元测试到复杂的功能测试。显著特点是其简洁的语法,可以无需继承 TestCase 类直接使用函数来编写测试用例,并通过 assert语句 进行断言。还支持参数化测试、插件系统以及丰富的 assertion rewriting 功能,使编写测试更加便捷和直观。我们可以使用它进行自动化测试。
pytest 允许创建自定义的标记,以便组织和分类测试。
PS: 本文基于pytest 8.3.3,建议使用PyCharm编写并运行测试套件,方便。
使用文件自定义标记装饰器
pytest 可在 pytest.ini 文件中自定义 mark 标记装饰器:
在 pytest.ini 中定义标记
# pytest.ini
[pytest]
markers =
user: 标记用户功能冒烟测试。添加用户、编辑用户、删除用户 (deselect with '-m "not user"')
business_process(serial): 标记业务流程测试。serial为1代表购物流程,serial为2代表理财流程。
user_detailed: 标记用户功能的字符值及边界测试
(deselect with '-m "not user"')
这个注释的意思是:如果你想跳过所有标记为 user 的测试,你可以在运行 pytest 命令时加上 -m “not user” 选项。not 后面字符需与标记名一致。
还可以定义 business_process(serial): 标记业务流程测试。serial为1代表购物流程,serial为2代表理财流程。 (deselect with '-m "not business_process"')
,运行 pytest 命令时加上 -m "not business_process"
选项来跳过所有标记为 business_process 的测试。
把 pytest.ini 放在 Config 目录下:
Project/
│
├── Config/
│ └── pytest.ini # 自定义 mark 标记
│
├── Package/ # 程序目录
│ ├── __init__.py # 包初始化文件,可以定义一些变量或执行一些操作。当然里面什么都不写也可以。
│ ├── module1.py # 测试程序模块,比如连接数据库操作数据,接口请求等操作,推荐按功能封装成类
│ └── module2.py # 测试程序模块,比如连接数据库操作数据,接口请求等操作,推荐按功能封装成类
│
├── Test/ # 测试用例目录
│ ├── __init__.py # 包初始化文件
│ ├── test_module1.py # 测试 module1 的测试用例
│ └── test_module2.py # 测试 module2 的测试用例
├── app.py # 项目启动文件
├── requirements.txt # 项目依赖项列表
└── README.md # 项目说明文档
在app.py中指定 pytest.ini 的路径:
# app.py
import pytest
import sys
import logging
import argparse
# 设置日志
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
def run_tests(test_target):
# 指定 pytest.ini 的路径并运行测试用例
pytest_args = ["-c", "Config/pytest.ini", "-v", test_target]
try:
# 运行测试
exit_code = pytest.main(pytest_args)
# 根据退出码判断测试是否成功
exit_messages = {
0: "全部测试用例通过",
1: "部分测试用例未通过",
2: "测试过程中有中断或其他非正常终止。",
3: "内部错误",
4: "pytest无法找到任何测试用例",
5: "pytest遇到了命令行解析错误"
}
logging.info(exit_messages.get(exit_code, "未知的退出码"))
except Exception as e:
logging.error(f"运行测试时发生错误: {e}")
return 1
return exit_code
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="使用指定的命令运行 pytest 测试。")
parser.add_argument('test_target', nargs='?', type=str, default="Test/", help='指定运行命令。 (默认: Test/)')
args = parser.parse_args()
exit_code = run_tests(args.test_target)
sys.exit(exit_code)
除了在 pytest.ini 自定义外,还有其它方法,这里就不介绍了,感兴趣可以访问官方文档:Working with custom markers。
在测试用例中使用自定义标记
编写测试用例并使用自定义标记进行装饰:
# test_module1.py
import pytest
@pytest.mark.user
def test_weird_simple_case():
assert [1]
@pytest.mark.business_process(serial=1)
def test_weird_simple_case1():
assert [1]
@pytest.mark.business_process(serial=2)
def test_weird_simple_case2():
assert [2]
@pytest.mark.user_detailed
def test_weird_simple_case3():
assert [2]
@pytest.mark.user
@pytest.mark.user_detailed
def test_weird_simple_case4():
assert [2]
终端中运行标记为 user 的测试用例:
cd C:\PythonTest\
pytest Test\ -c "Config/pytest.ini" -m user
整合进 app.py 里:
# app.py
import pytest
import sys
import logging
import argparse
# 设置日志
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
def run_tests(test_target, mark):
if mark == None:
# 指定 pytest.ini 的路径并运行测试用例
pytest_args = ["-c", "Config/pytest.ini", "-v", test_target]
else:
# 指定 pytest.ini 的路径并运行指定标记的测试用例
pytest_args = ["-c", "Config/pytest.ini", "-v", test_target, "-m", mark]
try:
# 运行测试
exit_code = pytest.main(pytest_args)
# 根据退出码判断测试是否成功
exit_messages = {
0: "全部测试用例通过",
1: "部分测试用例未通过",
2: "测试过程中有中断或其他非正常终止。",
3: "内部错误",
4: "pytest无法找到任何测试用例",
5: "pytest遇到了命令行解析错误"
}
logging.info(exit_messages.get(exit_code, "未知的退出码"))
except Exception as e:
logging.error(f"运行测试时发生错误: {e}")
return 1
return exit_code
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="使用指定的命令运行 pytest 测试。")
parser.add_argument('test_target', nargs='?', type=str, default="Test/", help='指定运行命令。 (默认: Test/)')
# 命令行新增参数-mark,可选参数
parser.add_argument('-m', '--mark', nargs='?', type=str, help='指定运行命令。 (默认: Test/)')
args = parser.parse_args()
exit_code = run_tests(args.test_target, args.mark)
sys.exit(exit_code)
终端启动:
- 运行标记为 user 的测试用例:
cd C:\PythonTest\
python .\app.py -m user
- 运行非 user 标记的测试用例:
cd C:\PythonTest\
python .\app.py -m "not user"
- 运行标记为 business_process(serial=2) 的测试用例:
cd C:\PythonTest\
python .\app.py -m "business_process(serial=2)"
- 运行标记为 business_process(serial=2) 的测试用例和标记为 business_process(serial=1) 的测试用例:
cd C:\PythonTest\
python .\app.py -m "business_process(serial=2) or business_process(serial=1)"
- 运行标记为 user 并同时标记为 user_detailed的测试用例:
cd C:\PythonTest\
python .\app.py -m "user and user_detailed"
PyCharm中启动:
只需要在步骤七输入相关参数,针对不同的测试策略,就可以创建不同的运行配置。
-m
参数不是必须的,可以为空去运行指定文件中的测试用例或指定测试用例,参考测试套件的目录结构。
标记测试组和测试模块
同样可以标记测试组或整个模块:
标记测试组:
# test_module1.py
import pytest
# 可以添加多个标记 @pytest.mark.business_process
@pytest.mark.user
class TestCase:
def test_case1(self):
assert [1]
def test_case2(self):
assert [1]
def test_case3(self):
assert [2]
标记整个模块:
# test_module1.py
import pytest
# 可以添加多个标记 pytestmark = [pytest.mark.user, pytest.mark.user_detailed]
pytestmark = pytest.mark.user
def test_case1():
assert [1]
def test_case2():
assert [1]
def test_case
将可调用对象传递给自定义标记
pytest 提供方法,使用可调用函数作为标记的参数:
import pytest
def hello_world(*args, **kwargs):
return "Hello World"
# 自定义标记的使用
@pytest.mark.business_process.with_args(hello_world, serial=1)
def test_custom_mark(request):
# 在测试函数内部,可以通过 request.node.get_closest_marker 获取标记信息
marker = request.node.get_closest_marker('business_process')
if marker:
func = marker.args[0] # 获取传递给标记的第一个参数,即我们的hello_world函数
result = func() # 调用函数
assert result == "Hello World" # 断言结果
python .\app.py -m business_process ,运行结果:
============================= test session starts =============================
collecting ... collected 1 item
test_mod.py::test_custom_mark PASSED [100%]
======================== 1 passed, 1 warning in 0.01s =========================
实际应用中,可以在被调用的函数中构建测试需要的操作,满足个性化要求。
实际测试中运用自定义标记
假设有一个书籍下载应用服务,包含四个模块:用户管理、登录、查询、下载。用户管理包括注册、修改密码、注销等功能,查询支持出版时间、作者、书名、出版社等条件查询,下载则支持各种格式。
就可以这样定义:
# pytest.ini
[pytest]
markers =
business_process: 标记业务流程测试。包括用户注册、登录、查询书籍和下载书籍。 (deselect with '-m "not business_process"')
user(serial): 标记用户功能测试。注册用户(1)、修改密码(2)、注销用户(3) (deselect with '-m "not user"')
select: 标记查询功能测试。 (deselect with '-m "not select"')
download:标记下载功能测试。 (deselect with '-m "not download"')
以上只值得参考,请根据实际自定义标记。
THEEND
© 转载需要保留原始链接,未经明确许可,禁止商业使用。CC BY-NC-ND 4.0
...