【pytest】使用fixture装饰器实现数据的前置和后置

694 阅读5分钟

一.前言

在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中返回值

执行效果: image.png 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('数据释放-后置操作')

执行效果:

image.png

name的使用(不常用): 被fixture装饰函数的名称,我们也可以用name参数来替换,相当于一个别名,当然调用的时候也要用name对应的名称:

image.png 注:除非是一些特殊场景,非必要可以不使用name这个参数

conftest.py配合fixture装饰器使用

在上面的例子中,我们都是将fixture装饰器写在具体的case中的,但是这样有局限性,因为只对当前的case生效,但是往往测试环境中,很多case需要一些相同的全局的前后置操作,比如全局登录、数据库对象建立和销毁等,此时就需要将这些共有的前置或后置的功能抽出来,封装成共有的fixture方法,那么conftest.py文件就排上用场了

文件的建立:需要在使用的文件夹下,建立一个conftest.py的文件(不能自定义名称),该文件夹子下的所有case都可以直接使用,无需import导入,建立好conftest.py文件后,里面就可以将我们需要的前置或后置的方法用fixture方法封装,定义和使用与之前并无差别

例:conftest.py文件与case放在同一目录下:

image.png

fixture装饰器封装的数据驱动写在conftest.py中:

image.png

需要调用的case:

image.png 执行效果:

image.png