一、fixture介绍
fixture是pytest特有的功能,它用pytest.fixture标识,定义在函数前面。在你编写测试函数的时候,你可以将此函数名称作为传入参数,pytest将会以依赖注入方式,将该函数的返回值作为测试函数的传入参数。
fixture有明确的名字,在其他函数,模块,类或者整个工程调用它时被激活。
fixture是基于模块来执行的,每个fixture的名字就可以触发一个fixture的函数,它自身也可以调用其他的fixture。
我们可以把fixture看作是资源,在你的测试用例执行之前需要去配置这些资源,执行完成后需要去释放资源。比如module类型的fixture,适合于那些许多测试用例都只需要执行一次的操作。
fixture还提供了参数化功能,根据配置和不同组件来选择不同的参数。
fixture主要的目的是为了提供一种可靠和可重复性的手段去运行那些最基本的测试内容。比如在测试网站的功能时,每个测试用例都要登陆和退出,利用fixture就可以只做一次,否则每个测试用例都要做这两步也是冗余。
下面会反复提高python的module概念,python中的一个module对应的就是一个.py文件。其中定义的所有函数或者变量都属于这个module。这个module对于所有函数而言就相当于一个全局的命名空间,而每个函数又都有自己局部的命名空间。
二、fixture基础实例入门
把一个函数定义为fixture很简单,只要在函数声明之前加上“@pytest.fixture”即可。其他函数要来调用这个fixture,只用把它当作一个输入的参数即可。
文件名称:test_fixture_basic.py
1 import pytest 2 3 @pytest.fixture() 4 def before(): 5 print ('nbefore each test') 6 7 def test_1(before): 8 print ('test_1()') 9 10 def test_2(before): 11 print ('test_2()') 12 assert 0
运行结果如下图:test_1和test_2运行之前都调用了before,也就是before执行了两次。默认情况下,fixture是每个测试用例如果调用了该fixture就会执行一次的。
三、调用fixture的三种方式
1. 在测试用例中直接调用它,例如第二部分的基础实例
2. 用fixture装饰器调用fixture
可以用以下三种不同的方式来写,我只变化了函数名字和类名字,内容没有变。第一种是每个函数前声明,第二种是封装在类里,类里的每个成员函数声明,第三种是封装在类里在函数前声明。可以看到三种不同方式的运行结果都是一样的。
文件名称:test_fixture_decorator.py
1 import pytest 2 3 @pytest.fixture() 4 def before(): 5 print(' before each test') 6 7 @pytest.mark.usefixtures("before") 8 def test_1(): 9 print('test_1()') 10 11 @pytest.mark.usefixtures("before") 12 def test_2(): 13 print('test_2()') 14 15 class Test1: 16 @pytest.mark.usefixtures("before") 17 def test_3(self): 18 print('test_1()') 19 20 @pytest.mark.usefixtures("before") 21 def test_4(self): 22 print('test_2()') 23 24 @pytest.mark.usefixtures("before") 25 class Test2: 26 def test_5(self): 27 print('test_1()') 28 29 def test_6(self): 30 print('test_2()')
运行结果如下图:和上面的基础实例运效果一样。
3. 用autouse调用fixture
fixture装饰器的一个optional可选的参数autouse,默认设置为False.
1 源代码如下: 2 def fixture(scope="function", params=None, autouse=False, ids=None, name=None): 3 if callable(scope) and params is None and autouse is False: 4 # direct decoration 5 return FixtureFunctionMarker("function", params, autouse, name=name)(scope) 6 if params is not None and not isinstance(params, (list, tuple)): 7 params = list(params) 8 return FixtureFunctionMarker(scope, params, autouse, ids=ids, name=name)
当默认False时就可以用上面两种方式调用fixture。
当设置True时,在一个session内的所有的test都会自动调用这个fixture。权限大,责任也大,所以用该功能时也要谨慎小心。
1 mport time 2 import pytest 3 4 @pytest.fixture(scope="module", autouse=True) 5 def mod_header(request): 6 print(' -----------------') 7 print('module : %s' % request.module.__name__) 8 print('-----------------') 9 10 @pytest.fixture(scope="function", autouse=True) 11 def func_header(request): 12 print(' -----------------') 13 print('function : %s' % request.function.__name__) 14 print('time : %s' % time.asctime()) 15 print('-----------------') 16 17 def test_one(): 18 print('in test_one()') 19 20 def test_two(): 21 print('in test_two()')
但是对于不熟悉自己组的测试框架的人来说,在pytest里面去新写测试用例,需要去了解是否已有一些fixture是module或者class级别的需要注意。
四、fixture scope
function:每个test都运行,默认是function的scope
class:每个class的所有test只运行一次
module:每个module的所有test只运行一次
session:每个session只运行一次
比如你的所有test都需要连接同一个数据库,那可以设置为module,只需要连接一次数据库,对于module内的所有test,这样可以极大的提高运行效率。
五、fixture返回值
在上面的例子中,fixture返回值都是默认None,我们可以选择让fixture返回我们需要的东西。如果你的fixture需要配置一些数据,读个文件,或者连接一个数据库,那么你可以让fixture返回这些数据或资源。
如何带参数
fixture还可以带参数,可以把参数赋值给params,默认是None。对于param里面的每个值,fixture都会去调用执行一次,就像执行for循环一样把params里的值遍历一次。
test_fixture_param.py
1 import pytest 2 3 @pytest.fixture(params=[1, 2, 3]) 4 def test_data(request): 5 return request.param 6 7 def test_not_2(test_data): 8 print('test_data: %s' % test_data) 9 assert test_data != 2
可以看到test_not_2里面把用test_data里面定义的3个参数运行里三次。