fixture
fixture
是指夹具(把用例夹在中间),它包括前置工作和后置工作,前置是用例代码的准备阶段,后置是用例执行之后的清理阶段,用例是放在前置代码和后置代码之间的
首先先定义一个fixture
函数
在unittest
框架中前置函数名叫做setup
,后置函数名叫做teardown
但是在**pytest
框架中并没有这个规则,它的前置函数名和后置函数名都可以自取**
如果说前置函数名和后置函数名可以随便取的话,那么要如何和普通函数分辨呢?——答案就是:在函数名前面加一个@pytest.fixture
这样说明这个就是一个夹具
而且前置代码和后置代码是放在同一个文件下,那么应该如何判断哪一部分是前置代码,哪一部分是后置代码呢?——答案就是使用yield
关键字作为分割线,它之前的属于前置代码,它之后的属于后置代码
fixture
夹具有几个作用域?之前提到过用例被夹在前置代码和后置代码之中,那么这中间到底夹的是一个一个的测试函数还是测试类,测试模块或者是说测试会话呢?中间夹什么是可以自己去设置的,最后只要去调用一下就可以。它一共有四个作用域:function
(测试函数)、class
(测试类)、module
(测试模块)、session
(测试会话) PS:夹具的函数名没有要求,可以按照需求取
测试函数的执行结果模板:
前置函数…
一个函数执行
后置函数…
前置函数…
第二个函数执行
后置函数…
…(直到所有的函数执行完)
例如:
import pytest
@pytest.fixture
def go_try():
print('我是前置代码')
yield
print('我是后置代码')
@pytest.mark.usefixtures('go_try')
def test_001_try():
print('test new...')
@pytest.mark.usefixtures('go_try')
def test_002_try():
print('test new1...')
@pytest.mark.usefixtures('go_try')
def test_003_try():
print('test new2...')
运行结果:
测试类的执行结果模板:
前置函数…
一个类执行
后置函数…
前置函数…
另一个类执行
后置函数…
…(直到所有的类执行完成)
例如:
import pytest
@pytest.fixture(scope='class')
def go_try():
print('我是前置测试类代码')
yield
print('我是后置测试类代码')
@pytest.mark.usefixtures('go_try')
class Test_001_try:
def test_001_try(self):
print('test new...')
def test_002_try(self):
print('test new1...')
def test_003_try(self):
print('test new2...')
@pytest.mark.usefixtures('go_try')
class Test_002_try:
def test_003_try(self):
print('test new3...')
def test_004_try(self):
print('test new4...')
def test_005_try(self):
print('test new5...')
scope
参数用来设置作用域
运行结果:
测试函数的夹具初始化是:@pytest.fixture
,然后里面加上函数。想要使用时前面加@pytest.mark.usefixtures('夹具函数名称')
测试类的夹具初始化是:@pytest.fixture(scope='class')
,然后里面加上函数。想要使用时前面加@pytest.mark.usefixtures('夹具函数名称')
测试模块的夹具初始化是:@pytest.fixture(scope='module')
,然后里面加上函数。想要使用时前面加@pytest.mark.usefixtures('夹具函数名称')
测试会话的夹具初始化是:@pytest.fixture(scope='session')
,然后里面加上函数。想要使用时前面加@pytest.mark.usefixtures('夹具函数名称')
PS!!!补充一个特别重要的点:如果说想要使用夹具,要自己根据是否符合前置条件和后置条件,然后再主动加上@那一部分,不加是不会被夹进去的
例如:
import pytest
@pytest.fixture(scope='class')
def go_try():
print('我是前置测试类代码')
yield
print('我是后置测试类代码')
@pytest.mark.usefixtures('go_try')
class Test_001_try:
def test_001_try(self):
print('test new...')
def test_002_try(self):
print('test new1...')
def test_003_try(self):
print('test new2...')
class Test_002_try:
def test_003_try(self):
print('test new3...')
def test_004_try(self):
print('test new4...')
def test_005_try(self):
print('test new5...')
前一段代码中第一个类使用了夹具,第二个类也用了。而现在这段代码中取消第二个类的夹具,结果:
很显然没有使用夹具,但是测试用例依旧会执行
而且测试函数和测试函数等可以同时使用
例如:
import pytest
@pytest.fixture(scope='class')
def go_try():
print('我是前置测试类代码')
yield
print('我是后置测试类代码')
@pytest.fixture
def go_to_try():
print('我是前置测试类代码')
yield
print('我是后置测试类代码')
@pytest.mark.usefixtures('go_to_try')
@pytest.mark.usefixtures('go_try')
class Test_001_try:
def test_001_try(self):
print('test new...')
def test_002_try(self):
print('test new1...')
def test_003_try(self):
print('test new2...')
在上述代码中定义了两个夹具,一个夹测试函数,一个夹测试类
在class
前两个都使用了,所以它得到的结果会是在最外面包一层测试类的夹具,其中的每一个函数又包一层测试函数的夹具:
只要前置代码没有问题,一般都能直接走到后置代码
因为如果说不是前置代码出现了错误 ,那么不管用例成功与否都会被执行出来,不会说用例失败了就中断了,最后依旧走到后置阶段
前置阶段准备工作产生的数据应该如何被测试用例使用?
因为测试用例和fixture
部分是分开的,所以fixture
部分得到的数据是不能直接被测试用例使用的。
首先在fixture
部分,直接在yield
后面加上要返回的数据名
比如在下面这个代码中要将前置部分产生的随机数字传给测试用例:
import pytest
import uuid
@pytest.fixture(scope='class')
def go_try():
print('我是前置测试类代码')
token = uuid.uuid4()
yield token
print('我是后置测试类代码')
@pytest.mark.usefixtures('go_try')
class Test_001_try:
def test_001_try(self):
print('test new...')
def test_002_try(self):
print('test new1...')
def test_003_try(self):
print('test new2...')
其中的uuid
是一个模块需要被导入,uuid.uuid4()
是用来生成随机数的。上述代码中token
用来接收产生的随机数,然后现在想要将这个值传递给测试用例,那么先在yield
后面加上想要返回的变量名
但是这样测试用例那边还没有接收这个数据,那么测试用例怎么去接收呢?
还是以上面代码为例,加上需要的部分:
import pytest
import uuid
@pytest.fixture(scope='class')
def go_try():
print('我是前置测试类代码')
token = uuid.uuid4()
print('token返回的值是:',token)
yield token
print('我是后置测试类代码')
@pytest.mark.usefixtures('go_try')
class Test_001_try:
def test_001_try(self,go_try):
print('token返回的值是:',go_try)
def test_002_try(self):
print('test new1...')
def test_003_try(self):
print('test new2...')
测试用例接收的方法就是 在需要该测试用例的函数 在进行定义的时候 在self
后面 加上夹具中函数的名字 用该名字来接收 夹具函数的返回值
那么如果有多个返回值能否只接收其中一个值呢?
答案是不能的,用yield
返回多个值后,如果后面某个函数进行了调用,那么这几个值已经都被接收了。(返回多个值的话 就是在yield
后面 直接加变量名 后面再加逗号 再加另外的变量就行)
比如:
import pytest
import uuid
@pytest.fixture(scope='class')
def go_try():
print('我是前置测试类代码')
token = uuid.uuid4()
print('token返回的值是:',token)
yield token,'1,2,3'
print('我是后置测试类代码')
@pytest.mark.usefixtures('go_try')
class Test_001_try:
def test_001_try(self,go_try):
print('token返回的值是:',go_try)
def test_002_try(self):
print('test new1...')
def test_003_try(self):
print('test new2...')
此处的test_001_try
函数调用了夹具中定义的函数,所以它接收到了token
和"1,2,3"
。所以说多个返回值如果被调用了都是一起的。
那么,多个返回值一起返回,返回值会是什么类型呢?
例如:
import pytest
import uuid
@pytest.fixture(scope='class')
def go_try():
print('我是前置测试类代码')
token = uuid.uuid4()
yield token,'1,2,3'
print('我是后置测试类代码')
@pytest.mark.usefixtures('go_try')
class Test_001_try:
def test_001_try(self,go_try):
print('go_try返回的值是:',go_try)
print('go_try返回值的类型是:',type(go_try))
def test_002_try(self):
print('test new1...')
def test_003_try(self):
print('test new2...')
该段代码中倒数5,6行 通过运行结果就可以知道 夹具中函数go_try
返回的是什么类型
运行结果:
根据结果:显然 多个返回值一起 返回得到的 返回值的类型是元组
如果不想一起以元组的形式返回,想单独返回每一个数据,有一种方法:
用多个变量名去接收函数的多个返回值:
import pytest
import uuid
@pytest.fixture(scope='class')
def go_try():
print('我是前置测试类代码')
token = uuid.uuid4()
yield token,'1,2,3'
print('我是后置测试类代码')
@pytest.mark.usefixtures('go_try')
class Test_001_try:
def test_001_try(self,go_try):
a,b = go_try
def test_002_try(self):
print('test new1...')
def test_003_try(self):
print('test new2...')
第12行代码中就是用a和b
两个变量去接收返回值,a
得到第一个数据,b
得到第二个数据 (注意:只有元组有这种用法,其他数据类型没有)
fixture
的共享机制在前面的介绍中,fixture
和用例是放在一个模块下的。如果说将fixture
单独放在一个模块下,那么用例应该如何去使用这个夹具呢?——答案就是在用例的父级目录下新建一个名为conftest.py
的文件,注意这里的文件名字是不可以改变的!!!将定义夹具fixture
的代码放到里面。
存放用例的模块直接使用就行,不用去调用,因为它有个机制会自动在父级目录下寻找conftest.py
文件。
值得注意的是该conftest.py
模块的作用域只在它所在的文件夹下面,超出这个它所在的文件夹去使用它就会识别不到
例如:
该conftest.py
文件在pythonProject2
文件夹下,那么只有在这个文件夹里面的文件可以使用该conftest.py
文件
例如在test_02.py
文件里面的用例想要使用夹具:
从图中易看出在test_02.py
中可以直接去使用,不用去调用什么的
结果:
当然不同的文件夹下可以新建多个conftest.py
文件。如果说一个存放用例的模块想要使用夹具,那么它就会去寻找conftest.py
文件,有多个conftest.py
文件的话,它寻找的方向就是:先看该用例的模块下有没有夹具,没有的话就在同一文件夹下找,找不到就一级一级往上找,找到了就会停止。
fixture
并不是一定要共享,不需要共享的夹具可以就写在当下用例的目录下。不同的文件下写conftest.py
文件,虽然看上去有些乱,好像不知道到底要使用哪个conftest.py
文件,但是实际上,哪些用例需要使用哪些夹具我们自己是可以控制的。一般写在根目录上的conftest.py
文件是全局共享的,可能整个项目的用例都需要一样的前置准备工作和后置清理工作。