一.前言
在pytest框架中,编写case时,有常用的2种方法来满足用例的数据驱动需求,它们分别是:
第一种
#setup: 在每条用例前执行,如:数据准备
#teardown: 在每条用例后执行,如:销毁数据
第二种
setup_class:
在所有用例前执行一次,用于不需要重复创建的场景,如:创建日志、数据库连接、创建接口请求对象
teardown_class:
在所有用例后执行一次 用于不需要重复创建的场景,如:销毁数据库连接、销毁请求对象
但是这两种不能适用灵活多变的测试场景,此时就需要引入pytest下的fixture装饰器来满足我们灵活多变的测试需求
二.fixture装饰器
fixture装饰器的参数构成有: pytest.fixture(scope,params,autouse,ids,name)
参数1.scope:表示的是fixture的作用域,可以是函数、类、模块、package, 默认作用域function
参数2.parmars:参数项,支持字典、元组、列表、字典列表、字典元组
参数3.autouse:值为布尔值,为ture时表示自动执行,为false时表示需要手动调用,默认false
参数4.ids:当使用parmars时参数化时,为参数的别名
参数5.name:给标记的方法取一个别名
scope和autouse参数一般搭配使用,用来控制执行情况
- 当scope='class',autouse=True时,表示作用域在类上,且自动执行,这样的参数设置等价于setup_class和teardown_class
@pytest.fixture(scope='class', autouse=True)
def my_fixture():
print('数据准备-前置操作')
yield
print('数据释放-后置操作')
class Test_fixture():
def test_01(self):
print('案例1已执行')
def test_02(self):
print('案例2')
实际的运行结果为:
2.py::Test_fixture::test_01 数据准备-前置操作
案例1已执行 PASSED
2.py::Test_fixture::test_02 案例2已执行
数据释放-后置操作 PASSED
- 当scope='module',autouse=True时,表示作用域在当前的py文件上,且自动执行,不管当前的py文件里面有多少个class,都只会运行一次,这样的参数设置也等价于setup_class和teardown_class
@pytest.fixture(scope='module', autouse=True)
def my_fixture():
print('数据准备-前置操作')
yield
print('数据释放-后置操作')
class Test_fixture():
def test_01(self):
print('案例1已执行')
def test_02(self):
print('案例2')
实际的运行结果为:
2.py::Test_fixture::test_01 数据准备-前置操作
案例1已执行 PASSED
2.py::Test_fixture::test_02 案例2已执行
数据释放-后置操作 PASSED
- 当scope='function',autouse=True时,表示作用域在函数上,且自动执行,这样的参数设置等价于setup和teardown,当作用在函数上时,则scope='function'可以省略不写
@pytest.fixture(scope='function', autouse=True)
def my_fixture():
print('数据准备-前置操作')
yield
print('数据释放-后置操作')
class Test_fixture():
def test_01(self):
print('案例1已执行')
def test_02(self):
print('案例2已执行')
实际的运行结果为:
2.py::Test_fixture::test_01 数据准备-前置操作
案例1已执行
数据释放-后置操作 PASSED
2.py::Test_fixture::test_02 数据准备-前置操作
案例2已执行
数据释放-后置操作 PASSED
- 当scope='function',autouse=False时,表示作用域在函数上,但是此时不会自动执行,需要我们手动去调用,根据是否有返回值,这里有两种调用方式:
@pytest.fixture(scope='function', autouse=False)
def my_fixture():
print('数据准备-前置操作')
yield
print('数据释放-后置操作')
class Test_fixture():
# 第一种.直接在函数的参数中使用我们自定义的fixture函数名,以形参形式传入,这种情况可以适用于fixture有返回值的情况
def test_01(self, my_fixture):
print('案例1已执行')
# 第二种.使用pytest的mark调用,传入我们自定义fixture函数名,以自字符串形式传入,所以需要引号,这种情况适用于fixture没有返回值的情况
@pytest.mark.usefixtures('my_fixture')
def test_02(self):
print('案例2已执行')
parmars参数的用法
params参数中可以是:字典、元组、列表、字典列表、字典元组
@pytest.fixture(scope='function', autouse=False, params=['参数1','参数2'])
def my_fixture(request):
print('数据准备-前置操作')
yield request.param
print('数据释放-后置操作')
class Test_fixture():
def test_01(self, my_fixture):
print(my_fixture)
print('案例1已执行')
上面代码中,params列表中有2个参数,被引用的函数会执行2次,因为要分别传入2次参数,接收参数时可以引入request参数,接收的值为:request.param,因为列表中有2个参数,所以request.param也会接收2次参数。
而手动调用的形参my_fixture则直接对应的是fixture中返回值
执行效果:
parmars参数和ids参数配合使用:
ids的作用是当fixture装饰器使用了params参数时,给予参数一个别名,因为参数调用的内容会展示为Unicode编码(如上图红色横线表示),当使用别名时(ids命名也要满足Unicode编码规则),则会清晰的展示当前参数的调用情况,所以对上面的代码进行改造后:
注:ids别名的使用也要和前面的params数量对应的上,否则会报错
@pytest.fixture(scope='function', autouse=False, params=['参数1','参数2'], ids=['param_1','param_2'])
def my_fixture(request):
print('数据准备-前置操作')
yield request.param
print('数据释放-后置操作')
执行效果:
name的使用(不常用): 被fixture装饰函数的名称,我们也可以用name参数来替换,相当于一个别名,当然调用的时候也要用name对应的名称:
注:除非是一些特殊场景,非必要可以不使用name这个参数
conftest.py配合fixture装饰器使用:
在上面的例子中,我们都是将fixture装饰器写在具体的case中的,但是这样有局限性,因为只对当前的case生效,但是往往测试环境中,很多case需要一些相同的全局的前后置操作,比如全局登录、数据库对象建立和销毁等,此时就需要将这些共有的前置或后置的功能抽出来,封装成共有的fixture方法,那么conftest.py文件就排上用场了
文件的建立:需要在使用的文件夹下,建立一个conftest.py的文件(不能自定义名称),该文件夹子下的所有case都可以直接使用,无需import导入,建立好conftest.py文件后,里面就可以将我们需要的前置或后置的方法用fixture方法封装,定义和使用与之前并无差别
例:conftest.py文件与case放在同一目录下:
fixture装饰器封装的数据驱动写在conftest.py中:
需要调用的case:
执行效果: