当前位置:   article > 正文

03 pytest fixture(夹具)_testfixure测试夹具

testfixure测试夹具

前言

上一章介绍了pytest的setup和teardown,但是不够完善且灵活性不够强。设想一个场景,我们在一个class中有5条用例,2条需要登录、3条不需要登录、 1条需要数据库、4条不需要数据库,这种场景使用setup/teardown实现不是很方便。 对于这种场景可以试用fixture(夹具)来满足。

fixture 是什么

fixture是pytest用于将测试前后进行预备、清理工作的代码处理机制。
相较于setup和teardown而言,有以下几点优势:

  1. fixture命名更加灵活,局限性较小
  2. conftest.py配置里面可以实现数据共享,不需要import就能自动找到一些配置
  3. 使用更加灵活,当 pytest 运行测试时,它会查看该测试函数签名中的参数,然后搜索与这些参数同名的固定装置。一旦 pytest 找到它们,它就会运行这些固定装置,捕获它们返回的内容(如果有的话),并将这些对象作为参数传递到测试函数中。

基本使用

在case文件test_01_baseuse.py 中定义并使用

步骤

  1. 使用pytest.fixture 定义一个fixture
  2. 在测试case中通过传参的方式传入到case中
  3. case中使用这个fixture

示例

前后置都有

使用yield 关键字来实现fixture的前后置 yield前的为前置、 yield后的为后置

import pytest  # 导入pytest


@pytest.fixture()    # 使用装饰器的方式定义一个fixture
def my_fixture():
    print()
    print("==========before test===========")
    print("init data")
    data = "abc"
    yield data  # yield 之前是 case前之前, yield之后是 case后执行
    print("=========after test=========")
    print("clean data")


def test_01(my_fixture):  # 将要使用的fixture 传入测试case中
    c = my_fixture  
    print("========================data========================")
    print("c:", c)

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

执行结果
在这里插入图片描述

仅需要前置

将上述例子中的 yield后的代码去掉, yield换成return即可

import pytest

@pytest.fixture()
def my_fixture():
    print()
    print("==========before test===========")
    print("init data")
    data = "abc"
    return data   # 如果只需要前置 yield 改成return 即可


def test_01(my_fixture):  # 将要使用的fixture 传入测试case中
    c = my_fixture
    print("========================data========================")
    print("c:", c)

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

在这里插入图片描述

仅需要后置

yield 的之前不写代码 在yield后写代码即可

import pytest


@pytest.fixture()
def my_fixture():
    yield   # yield 之前是 case前之前, yield之后是 case后执行
    print("=========after test=========")
    print("clean data")


def test_03_teardown(my_fixture):  # 将要使用的fixture 传入测试case中
    c = my_fixture
    print("========================data========================")
    print("c:", c)

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

在这里插入图片描述

4种作用域

上边学习了 fixture的基本使用,现在来看下 fixture的作用域(类似 setup\teardown 相关的可以作用在类上 测试case上 模块上)
@pytest.fixture(scope=“function”) 默认的
@pytest.fixture(scope=“class”) 类中只执行一次
@pytest.fixture(scope=“module”) 每一个.py文件调用一次
@pytest.fixture(scope=“session”) 总的执行一次

介绍这些内容之前先简单介绍下conftest.py

conftest.py

conftest.py文件是pytest框架中的一个特殊文件,用于定义共享的设置、夹具(fixture)和钩子函数(hook)。
当pytest运行时,它会自动搜索项目中的conftest.py文件,并根据其中的定义来加载夹具和钩子函数。conftest.py文件可以位于项目的根目录下,也可以位于子目录中,它们会在对应的作用域内生效。

conftest.py文件特点:

  1. conftest.py文件名是固定的,不能修改
  2. contest.py文件不需要导入,pytest运行的时候会自动识别该文件
  3. conftest.py与运行的用例要在同一个pakage下,并且有__init__.py文件
  4. 放到项目的根目录下就可以全局目录调用,如果放到某个package下,那就在package内有效,可有多个conftest.py
  5. conftest.py作用于文件同级目录和子目录下的所有测试用例,当有多个conftest.py文件的时候,子目录的conftest.py文件优先级较高

scope=“function”

@pytest.fixture(scope=“function”) 是fixture默认的作用域 等效于 @pytest.fixture()
每一个函数或方法都会调用(前提是使用了这个fixture的),每个测试用例执行前都会执行一次function级别的fixture。
来看个例子
conftest.py (注意这里要以包的方式来写case即要有 init.py 文件) 中创建一个fixture

"""conftest"""
import pytest
import random

@pytest.fixture()  # 或者写 @pytest.fixture(scope="function")
def func_scope_fixture():
    r = random.random()
    print("===========调用一次产生一次随机数==========")
    print("r:=====", r)
    return r
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

在 测试文件中使用这个fixture

"""test"""

def test_04_1(func_scope_fixture):
    r = func_scope_fixture
    print("======test_04_1======")
    print(r)

def test_04_2(func_scope_fixture):
    r2 = func_scope_fixture
    print("======test_04_2======")
    print(r2)

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

执行结果
在这里插入图片描述
可以看到 使用到这个fixture的地方都重新执行了 产生了新的随机数

scope=“class”

每个测试类中只会调用一次(前提是测试类中使用了这个fixture)
看例子
conftest.py

@pytest.fixture(scope="class")
def cls_scope_fixture():
    r = random.random()
    print("===========同一个类中多次调用只会产生一次随机数==========")
    print("r:=====", r)
    return r
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

在 测试文件中使用这个fixture


class TestClsScope(object):
    def test_01(self, cls_scope_fixture):
        print("=======test_01=======")
        print("r: ", cls_scope_fixture)

    def test_02(self, cls_scope_fixture):
        print("=======test_02=======")
        print("r: ", cls_scope_fixture)

    def test_03(self):
        print("=======test_03=======")
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

执行结果
在这里插入图片描述
在这个类中只执行了一次 在这个类中再次使用的时候返回上次产生的值

scope=“module”

module 即一个py文件,就是说如果你在一个测试文件中引用了多次同一个fixture 那么这个fixture只在这个py文件中执行一次,其他都是使用这一次的结果
conftest.py

@pytest.fixture(scope="module")
def module_scope_fixture():
    r = random.random()
    print("===========同一个文件中多次调用只会产生一次随机数==========")
    print("r:=====", r)
    return r
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

在 测试文件中使用这个fixture


def test_mod_01(module_scope_fixture):
    print("======test_mod_01======")
    print(module_scope_fixture)


def test_mod_02(module_scope_fixture):
    print("======test_mod_02======")
    print(module_scope_fixture)


class TestModuleScope(object):
    def test_01(self, module_scope_fixture):
        print("======test_cls_mod_01======")
        print(module_scope_fixture)

    def test_02(self, module_scope_fixture):
        print("======test_cls_mod_02======")
        print(module_scope_fixture)

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

执行结果
在这里插入图片描述

在这个测试文件中只执行了一次 文件中其他case再次使用的时候返回上次产生的值

scope=“session”

当fixture的scope定义为session时,是指在当前目录下的所有用例之前和之后执行fixture对应的操作

fixture为session级别是可以跨.py模块调用的,也就是当我们有多个.py文件的用例的时候,如果多个用例只需调用一次fixture,那就可以设置为scope=“session”,且写到conftest.py文件里
来看例子

conftest.py

@pytest.fixture(scope="session")
def session_scope_fixture():
    t = time.time()
    print("current_time:---->", t)
    return t
  • 1
  • 2
  • 3
  • 4
  • 5

测试文件1


def test_session_1(session_scope_fixture):
    print("=======test_session_1========")
    print(session_scope_fixture)
  • 1
  • 2
  • 3
  • 4

测试文件2


def test_session_2(session_scope_fixture):
    print("=======test_session_2========")
    print(session_scope_fixture)
  • 1
  • 2
  • 3
  • 4

运行结果
在这里插入图片描述
可以看到在不同的测试文件中 只执行了一次

fixture的嵌套

在编写fixture时可以通过传参的方式传入已有的fixture,来实现新的功能

来看例子

@pytest.fixture(scope="session")
def session_scope_fixture():
    t = time.time()
    print("current_time:---->", t)
    return t


@pytest.fixture(scope="session")
def session_scope_date(session_scope_fixture):
    dt = datetime.fromtimestamp(session_scope_fixture).date()
    print("========current dt=======:", dt)
    return dt
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

使用和上边使用一样


def test_session_1(session_scope_date):
    print("=======test_session_1========")
    print(session_scope_date)
  • 1
  • 2
  • 3
  • 4

结果
在这里插入图片描述

fixture的 autouse 参数

对于所有 case 都用到的 fixture 如果每个用例都传入,会很麻烦。fixture 有个 autouse 参数,默认是False没开启的,可以设置为True开启自动使用fixture功能,这样用例就不用每次都去传参了,autouse设置为True,自动调用fixture功能。
所有用例都会生效,包括类中的测试用例和类以外的测试用例。
conftest.py

@pytest.fixture(autouse=True)
def auto_use_scope():
    print("===========每个 case 我都会执行==========")
    t = time.time()
    print("current_time:---->", t)
    return t
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
"""autouse"""
def test_autouse_01():
    print("test_autouse_01======")


def test_autouse_02():
    print("test_autouse_02======")


def test_autouse_03():
    print("test_autouse_03======")


def test_autouse_04():
    print("test_autouse_04======")
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

执行结果
在这里插入图片描述

fixtrue的 name 参数

给 fixture 起名,然后使用起的这个新名字来

import pytest

@pytest.fixture(name="new_fixture")
def fixture_rename():
    print("==========fixture_rename===")
    print("===========my name is new_fixture======")



def test_rename(new_fixture):
    print("========test_rename========")


@pytest.mark.usefixtures("new_fixture")
def test_rename2():
    print("========test_rename2========")

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

在这里插入图片描述

pytest params 参数

对于一个fixture场景我们有时候需要验证不同的输入的情况,那么可以使用 fixture 的params 传参来实现

import pytest 
@pytest.fixture(params=[{"name": "张三", "age": 21}, {"name": "李四", "age": 22}, {"name": "王五", "age": 25}])
def fix_params(request):

    return request.param


def test_param(fix_params):
    # print("fix_params: =====>", fix_params)
    print(f"我叫 {fix_params['name']}, 我今年{fix_params['age']}岁")
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

在这里插入图片描述

上述中虽然把所有的 param 都执行了,但是在测试结果中不能知道每次测试的输入是什么, 可以通过 ids 这个参数来给每次执行提供一个标记

import pytest
params = [{"name": "zs", "age": 21}, {"name": "ls", "age": 22}, {"name": "ww", "age": 25}]

@pytest.fixture(params=params, ids=[p['name'] for p in params])
def fix_params(request):
    return request.param


def test_param(fix_params):
    # print("fix_params: =====>", fix_params)
    print(f"我叫 {fix_params['name']}, 我今年{fix_params['age']}岁")
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

在这里插入图片描述

pytest 内置的 fixture

官方内置的 fixture:https://docs.pytest.org/en/7.4.x/reference/fixtures.html#fixture
在这里插入图片描述

用到的内置 fixture

介绍几个常用的吧

1、request

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/菜鸟追梦旅行/article/detail/672701
推荐阅读
相关标签