一、简介与安装
pytest 测试框架用于编写和自动发现与运行测试用例。pytest 8.0+ 要求 python 版本至少为 3.8+。
安装:
python
pip install pytest==8.3.2
实例:没有 pytest,测试用例需要放在 main 函数里执行。有 pytest,框架自动发现用例并执行。
main 函数:



pytest 框架:



二、用例自动发现与运行的规则
- 模块(文件):以 test_ 开头或以 _test 结尾。
- 类:以 Test 开头,且不能有 init 构造函数。
- 方法:以 test 开头。
三、命令参数
可以设置测试用例的运行范围、行为。

- 对方法打标签:@pytest.mark.xxx

对整个模块打标签:pytestmark = pytest.mark.xxx
- 参数也可以连写:pytest -s -v 或者 pytest -sv
示例:


四、配置文件
命令参数太长 ,每次运行都很繁琐。把命令参数放到配置文件pytest.ini,自动覆盖 pytest 默认行为。通常放在项目文件下。
注意:python_classes=Test*,如果有测试类才限制 Test*;没有测试类则不限制,执行所有。

markers两种格式:可读性好和差


示例:

python
[pytest]
;-m 只执行指定标记的用例
addopts = -vs -m smoke
testpaths = ./
python_files = test_*.py
python_classes = Test*
;声明标记
markers = smoke: 冒烟
五、断言
语法:
python
assert 条件, 错误信息
条件:布尔表达式
错误信息:若不满足条件时,显示的错误信息,可以没有。
作用:检查程序的值是否符合预期,不符合则抛异常 AssertionError。
六、参数化(类似循环执行)
1、pytest.mark.parametrize
(1)在方法上使用:对该方法传参。
python
@pytest.mark.parametrize("a, b", [(1, "aa"), (2, "bb")])
def test01(a, b):
print(f"测试用例1,{a},{b}")
运行结果:

(2)在类上使用:对该类的所有方法传参。
python
@pytest.mark.parametrize("a, b", [(1, "aa"), (2, "bb")])
class Test01:
def test02(self, a, b):
print(f"测试用例2,{a},{b}")
def test03(self, a, b):
print(f"测试用例3,{a},{b}")
(3)在全局使用:对模块下的所有方法传参。
标记整个模块+对模块下方法传参。
python
pytestmark = [pytest.mark.test0402, pytest.mark.parametrize("a, b", [(1, "aa"), (2, "bb")])]
2、@pytest.fixture(params=xxx)
python
@pytest.fixture(params=["a", "b"])
def data_provider(request):
return request.param
def test_06(data_provider):
print(f"测试用例6,{data_provider}")
结果:

3、传参方式,选择哪个
(1)pytest.mark.parametrize:适合单纯需要循环执行传参的。
(2)@pytest.fixture(params=xxx):有前置初始化、后置清理功能(适合资源管理,比如文件操作、数据库连接等);能在 fixture 定义的方法下对参数进行处理,适合需要生成动态数据的场景。
七、前后置
测试类不允许添加 init 方法,那么就需要其它的方式初始化。
1、setup_method 和 teardown_method
在类中的每个方法,都要执行一次前后置:
python
class Test02:
def setup_method(self):
print("执行一次方法前置")
def teardown_method(self):
print("执行一次方法后置")
def test_07(self):
print("执行测试用例7")
def test_08(self):
print("执行测试用例8")
结果:

2、setup_class 和 teardown_class
在类中,只对类执行一次前后置:
python
class Test03:
def setup_class(self):
print("执行一次方法前置")
def teardown_class(self):
print("执行一次方法后置")
def test_09(self):
print("执行测试用例9")
def test_10(self):
print("执行测试用例10")
结果:

3、fixture
(1)定义前置函数
python
@pytest.fixture
def login():
print("---执⾏登陆操作-----")
def test_list(login):
print("---访问列表⻚")
def test_detail(login):
print("---访问详情⻚")
结果:

(2)嵌套定义前置函数
也可以传多个定义的 fixture 前置函数。
python
@pytest.fixture
def first_entry():
print("执行 first_entry")
@pytest.fixture
def second_entry(first_entry):
print("执行 second_entry")
def test_11(second_entry):
print("执行 test_11")
结果:

(3)定义后置函数(yield fixture)
- 把 fixture 函数的 return 改为 yield:yield 前的为前置操作,yield 后的为后置操作。
- 当有嵌套fixture 函数时,前置操作执行顺序:first_entry >> second_entry,后置操作执行顺序:second_entry >> first_entry。
python
@pytest.fixture
def first_entry():
print("执行 first_entry 前置操作")
yield 1
print("执行 first_entry 后置操作")
@pytest.fixture
def second_entry(first_entry):
print("执行 second_entry 前置操作")
yield first_entry
print("执行 second_entry 后置操作")
def test_12(second_entry):
print(f"执行 test_12,{second_entry}")
结果:

(4)fixture 的参数
- scope:控制 fixture 方法的作用域范围。
- autouse:默认为 False。若填 True,则每个测试函数自动调用 fixture 方法。
- params:循环传参,见六、2。
- ids:给参数列表 params 的每个值取名。
- name:给 fixture 方法取名。
细说 scope:
- function(默认):带 fixture 参的每个测试函数都执行一次。
- class:每个类都执行一次。
- module:配合 conftest.py 使用。对每个模块执行一次前后置。
- session:配合 conftest.py 使用。对整个文件目录下,只执行一次。
conftest.py:配置文件,名字不能改,把 fixture 函数定义在配置文件。放在文件下,对文件下的模块起作用。