Pytest_fixture(9)
什么是fixture
fixture是pytest特有的功能,使用装饰器 @pytest.fixture 标记的函数在其他函数中能被当作参数传入并被调用。
fixture有明确的名字,在其他函数,模块,类或整个工程调用它时会被激活。
fixture是基于模块来执行的,每个fixture的名字就可以触发一个fixture的函数,它自身也可以调用其他的fixture。
fixture作用是为了提供一种可靠和可重复的手段去运行那些最基本的测试内容。比如在测试网站的功能时,每个测试用例都要登录和退出,利用fixture就可以只做一次,否则每个测试用例都要做这两步就显得很冗余。
fixture的参数
fixture的可用参数如下:
- scope:定义被fixture装饰方法的作用域。可用参数值如下:
- function(默认):作用域为每个函数或方法
- class:作用域为每个类。
- module:作用域为每个模块(每个py文件)。
- session:作用域为每次启动。
- autouse=True:自动执行被fixture装饰的方法。默认为False
- params:用于参数化,数据类型支持列表和元组。(比如:params=[“v1”, "v2", "v3"])
- ids:当使用params参数化时,将nodeid中的变量值替换为给定的值(很少使用)
- name:给被fixture标记的方法取别名。(很少使用,注:当取了别名后,原方法名就不可以用了)
scope参数完成前后置操作
function:作用于每个函数(与setup和teardown功能相似)
import pytest # function作用范围为每个函数或方法 @pytest.fixture(scope="function") def my_fixture(): # yield之前为前置操作,yield之后为后置操作 print("\n前置操作") yield print("\n后置操作") class TestPractice1: # 因为没使用autouse参数,使用时需要传入方法名 def test_001(self, my_fixture): print("执行用例001") def test_002(self): print("执行用例002") class TestPractice2: def test_003(self, my_fixture): print("执行用例003") def test_004(self): print("执行用例004")
执行结果
class:作用于每个类(与setup_class和teardown_class功能相似)
import pytest # class作用范围为每个类 @pytest.fixture(scope="class") def my_fixture(): # yield之前为前置操作,yield之后为后置操作 print("\n前置操作") yield print("\n后置操作") class TestPractice1: # 因为没使用autouse参数,使用时需要传入方法名 def test_001(self, my_fixture): print("执行用例001") def test_002(self): print("执行用例002") class TestPractice2: def test_003(self): print("执行用例003") def test_004(self, my_fixture): print("执行用例004")
执行结果
module:作用于每个模块
import pytest # module作用范围为每个模块 @pytest.fixture(scope="module") def my_fixture(): # yield之前为前置操作,yield之后为后置操作 print("\n前置操作") yield print("\n后置操作") class TestPractice1: # 因为没使用autouse参数,使用时需要传入方法名 def test_001(self, my_fixture): print("执行用例001") def test_002(self): print("执行用例002") class TestPractice2: def test_003(self): print("执行用例003") def test_004(self, my_fixture): print("执行用例004")
执行结果
session:作用于每次启动
autouse参数值为True时,被fixture装饰的函数自动被调用
上面我们使用被fixture装饰的函数,都需要将其函数名称当作参数传入到要使用的方法中。需要注意,自动执行范围与scope的作用域相关。
import pytest @pytest.fixture(autouse=True) def my_fixture(): # yield之前为前置操作,yield之后为后置操作 print("\n前置操作") yield print("\n后置操作") class TestPractice1: # 因为没使用autouse参数,使用时需要传入方法名 def test_001(self): print("执行用例001") def test_002(self): print("执行用例002") class TestPractice2: def test_003(self): print("执行用例003") def test_004(self): print("执行用例004")
执行结果
params用于参数化,数据类型支持列表和元组。
在上面的例子中,fixture返回值都是None,我们可以选择让fixture返回我们需要的东西。如果你的fixture需要配置一些数据,读个文件,或者连接一个数据库,那么你可以让fixture返回这些数据或资源。
实现步骤:
- 在fixture中使用params参数;
- 在被fixture装饰的函数中添加request,request是固定写法;
- 使用 yield 或 return 返回 request.param request.param是固定写法;
- 在要使用的方法中,添加被fixture装饰的函数名;
注意:参数化时,如果使用了autouse,每个方法都会执行一次参数化,所以autouse慎用。
import pytest @pytest.fixture(params=[1, 2, 3]) def my_fixture(request): # yield之前为前置操作,yield之后为后置操作 print("\n前置操作") yield request.param print("\n后置操作") class TestPractice1: # 参数化 def test_001(self, my_fixture): print("执行用例001") print(f"获取参数化的值:{my_fixture}") def test_002(self): print("执行用例002")
执行结果
如果没有前后置操作,只想做参数化操作,我们可以使用return返回 request.param
import pytest @pytest.fixture(params=[1, 2, 3]) def my_fixture(request): return request.param class TestPractice1: # 参数化 def test_001(self, my_fixture): print("执行用例001") print(f"获取参数化的值:{my_fixture}") def test_002(self): print("执行用例002")
执行结果
ids用于在参数化时,将nodeid中的变量值替换为给定的值
import pytest @pytest.fixture(params=[1, 2, 3], ids=["v1", "v2", "v3"]) def my_fixture(request): # yield之前为前置操作,yield之后为后置操作 return request.param class TestPractice1: # 参数化 def test_001(self, my_fixture): print("执行用例001") print(f"获取参数化的值:{my_fixture}") def test_002(self): print("执行用例002")
执行结果
name用于给被fixture标记的方法取别名
name用于给被fixture标记的方法取别名
import pytest @pytest.fixture(name="rename_fixture") def my_fixture(request): print("前置操作") yield print("后置操作") class TestPractice1: # 使用方法别名调用fixture def test_001(self, rename_fixture): print("执行用例001") def test_002(self): print("执行用例002")
需要注意是,使用该参数后,原函数名就不可用了。
fixture可以互相调用
import pytest @pytest.fixture() def fixture_1(): print("\nfixture1的前置") yield print("fixture1的后置") # 在fixture_2中调用fixture_1 @pytest.fixture() def fixture_2(fixture_1): print("fixture2的前置") yield print("\nfixture2的后置") class TestPractice1: # 调用fixture_2 def test_001(self, fixture_2): print("执行用例001") def test_002(self): print("执行用例002")
执行结果
一个用例可以调用多个fixture
import pytest @pytest.fixture() def fixture_1(): print("fixture1的前置") yield print("\nfixture1的后置") @pytest.fixture() def fixture_2(): print("\nfixture2的前置") yield print("fixture2的后置") class TestPractice1: # 调用多个fixture,执行顺序与调用顺序有关 def test_001(self, fixture_2, fixture_1): print("执行用例001") def test_002(self): print("执行用例002")
执行结果
conftest.py和@pytest.fixture结合使用设置全局可用
上面的演示,用例和fixture均是在同一个文件中,我们可以创建一个固定名称为conftest.py的文件,将其放在用例同级或上级以上目录,在这个文件中定义一个被fixture装饰的函数,这个函数可以在其下级任意目录,不需要import而直接使用。
这里有以下几点需要注意:
- 文件名称固定为conftest.py
- 在conftest.py定义的fixture函数不需要import就可以直接使用
- conftest.py影响的作用域为同级及其所有子目录
- conftest.py文件可以创建多个,当多个文件存在相同的fixture函数名是,最近的作用域函数功能生效