三、fixture
pytest 提供的 fixture 实现 unittest 中 setup/teardown 功能,可以在每次执行case之前初始化数据。
不同点是,fixture 可以只在执行某几个特定 case 前运行,只需要在运行 case 前调用即可,比 setup/teardown 使用起来更灵活。
fixture scope 作用范围
先看下 fixture 函数的定义:
def fixture(scope="function", params=None, autouse=False, ids=None, name=None): """ :arg scope: 可选四组参数:function(默认)、calss、module、package/session :arg params: 一个可选的参数列表,它将导致多个参数调用fixture函数和所有测试使用它。 :arg autouse: 如果为True,则fixture func将为所有测试激活可以看到它。如果为False(默认值),则需要显式激活fixture。 :arg ids: 每个参数对应的字符串id列表,因此它们是测试id的一部分。如果没有提供id,它们将从参数中自动生成。 :arg name: fixture的名称。 这默认为装饰函数的名称。
如果fixture在定义它的同一模块中使用,夹具的功能名称将被请求夹具的功能arg遮蔽; 解决这个问题的一种方法是将装饰函数命名 “fixture_ <fixturename>”然后使用”@ pytest.fixture(name ='<fixturename>')”。 """
重点说下 scope 四组参数的意义:
- function:每个方法(函数)都会执行一次。
- class:每个类都会执行一次。类中有多个方法调用,只在第一个方法调用时执行。
- module:一个 .py 文件执行一次。一个.py 文件可能包含多个类和方法。
- package/session:多个文件调用一次,可以跨 .py 文件。
1、通过参数引用:
class Test_ABC: @pytest.fixture() def before(self): print("------->before") def test_a(self,before): # ️ test_a方法传入了被fixture标识的函数,已变量的形式 print("------->test_a") assert 1 if __name__ == '__main__': pytest.main(["test_abc.py", "-s"])
执行结果:
test_abc.py ------->before # 发现before会优先于测试函数运行 ------->test_a
2、通过外部函数引用
import pytest @pytest.fixture() # fixture标记的函数可以应用于测试类外部 def before(): print("------->before") @pytest.mark.usefixtures("before") class Test_ABC: def setup(self): print("------->setup") def test_a(self): print("------->test_a") assert 1 if __name__ == '__main__': pytest.main(["test_abc.py", "-s"])
执行结果:
test_abc.py ------->before # 发现before会优先于测试类运行 ------->setup ------->test_a
3、设置为自动运行
import pytest @pytest.fixture(autouse=True) # 设置为默认运行 def before(): print("------->before") class Test_ABC: def setup(self): print("------->setup") def test_a(self): print("------->test_a") assert 1 if __name__ == '__main__': pytest.main(["test_abc.py", "-s"])
执行结果:
test_abc.py ------->before # 发现before自动优先于测试类运行 ------->setup ------->test_a
4、设置作用域为class
import pytest @pytest.fixture(scope='class',autouse=True) # 作用域设置为class,自动运行 def before(): print("------->before") class Test_ABC: def setup(self): print("------->setup") def test_a(self): print("------->test_a") assert 1 def test_b(self): print("------->test_b") assert 1 if __name__ == '__main__': pytest.main(["test_abc.py", "-s"])
执行结果:
test_abc.py ------->before # 发现只运行一次 ------->setup ------->test_a . ------->setup ------->test_b
5、返回值
import pytest @pytest.fixture(params=[1, 2, 3]) def need_data(request): # 传入参数request 系统封装参数 return request.param # 取列表中单个值,默认的取值方式 class Test_ABC: def test_a(self,need_data): print("------->test_a") assert need_data != 3 # 断言 if __name__ == '__main__': pytest.main(["test_abc.py", "-s"])
执行结果:
# 可以发现结果运行了三次 test_abc.py 1 ------->test_a
. 2 ------->test_a . 3 ------->test_a F
2.yield
用例执行完之后如需要 清除数据(或还原)操作,可以使用 yield 来实现。
#!/usr/bin/env python # coding=utf-8 import pytest @pytest.fixture(scope='function') def login(): print("登录") yield print("注销登录") def test_1(): print('测试用例1') def test_2(login): print('测试用例2') if __name__ =="__main__": pytest.main(['test_sample.py','-s'])
执行结果:
test_sample.py 测试用例1 . 登录 测试用例2 .注销登录 ============================== 2 passed in 0.08s ==============================
3.conftest
倘若有多个.py 文件需要调用 login() 方法,就必须把 login() 方法写在外面,这里引用了conftest.py 配置文件。
# conftest.py代码如下: import pytest @pytest.fixture(scope='function') def login(): print("登录") # test_sample.py 代码如下: import pytest def test_1(): print('测试用例1') def test_2(login): print('测试用例2') if __name__ =="__main__": pytest.main(['test_sample.py','-s'])
执行结果:
test_sample.py 测试用例1 . 登录 测试用例2 . ============================== 2 passed in 0.01s ==============================
内置 fixtures 之 pytestconfig:
pytestconfig 可以通过命令行参数、选项、配置文件、插件、运行目录等方式来控制pytest;
pytestconfig 实际上就是 request.config 的快捷方式,被称为“pytest 配置对象”;
我们最常使用的就是 getoption() 和getini():
1.getoption() 可以获取命令行的值;
def getoption(self, name: str, default=notset, skip: bool = False): """ return command line option value. """
2.getini() 可以获取 pytest.ini 配置文件的参数值;
1.使用 pytestconfig 获取命令行参数的值:
conftest.py:
import pytest # 注册自定义参数 cmdopt 到配置对象 def pytest_addoption(parser): parser.addoption("--cmdopt", action="store", default="None", help="将自定义命令行参数 ’--cmdopt' 添加到 pytest 配置中") # 其它fixture从配置对象获取 cmdopt 的值 @pytest.fixture(scope='session') def get_cmdopt(pytestconfig): return pytestconfig.getoption('--cmdopt') # 然后任何 fixture 或测试用例都可以调用 cmdopt 来获得设备信息
main.py:
device_info = {'id': 'xxx', 'name': '华为', 'version': '10'} pytest.main(['-m all', f'--cmdopt={device_info}']) # 使用自定义参数传参
2.使用 pytestconfig 获取 pytest.ini 配置文件中参数的值:
pytest.ini:
[pytest] markers = demo : marks tests as demo smoke: marks tests as smoke test : marks tests as test log_cli = 1
conftest.py:
@pytest.fixture(autouse=True) def fuc_fix(pytestconfig): print(pytestconfig.getini('markers')) print(pytestconfig.getini('log_cli'))