03 pytest fixture(夹具)

在这里插入图片描述

前言

上一章介绍了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)

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

仅需要前置

将上述例子中的 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)

在这里插入图片描述

仅需要后置

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)

在这里插入图片描述

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

在 测试文件中使用这个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)

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

scope=“class”

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

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

在 测试文件中使用这个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=======")

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

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

在 测试文件中使用这个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)

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

在这个测试文件中只执行了一次 文件中其他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


def test_session_1(session_scope_fixture):
    print("=======test_session_1========")
    print(session_scope_fixture)

测试文件2


def test_session_2(session_scope_fixture):
    print("=======test_session_2========")
    print(session_scope_fixture)

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

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

使用和上边使用一样


def test_session_1(session_scope_date):
    print("=======test_session_1========")
    print(session_scope_date)

结果
在这里插入图片描述

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
"""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======")

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

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========")

在这里插入图片描述

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']}岁")

在这里插入图片描述

上述中虽然把所有的 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']}岁")

在这里插入图片描述

pytest 内置的 fixture

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

用到的内置 fixture

介绍几个常用的吧

1、request

👉 官方文档

2、pytestconfig

👉 官方文档
pytestconfig是pytest的一个内置fixture,可以获取上下文,它的作用跟request.config是一样的,可以获取配置对象
pytestconfig中有2个比较常用的方法:

  1. getoption:获取命令行中的参数
  2. getini:获取pytest.ini配置文件中的参数
getoption

conftest.py

def pytest_addoption(parser):  # 增加传参 这里以--env为例
    # 添加命令行参数
    parser.addoption(
        "--env", default="test", choices=["dev", "test", "pre"], help="enviroment parameter")

在case中获取

def test_get_config(pytestconfig):
    env = pytestconfig.getoption("env")
    print("========env=====:", env

执行结果如下 拿到了命令行传参
在这里插入图片描述

getini

获取pytest.ini配置文件中的参数
默认pytest.ini中有一些已经定义好的参数 直接使用 pytestconfig.getini(“key”) 来获取就行了
比如markers、log_cli、python_files等这些
如果想添加自定义的配置参数看下边如何操作
1、先定义要添加的参数(也是使用pytest_addoption hook函数来完成)

def pytest_addoption(parser):  # pytest提供的增加参数的钩子函数 
    parser.addoption(    # 给命令行添加参数
        "--env", default="test", choices=["dev", "test", "pre"], help="enviroment parameter")
    parser.addini(	# 给配置文件中增加参数
        "base_url", type=None, default="https://www.example.com", help="base_url"
    )

2、在pytest.ini 中配置定义的参数

[pytest]
base_url="https://www.baidu.com"

3、在case中使用

"""pytestconfig"""


def test_get_config(pytestconfig):
    env = pytestconfig.getoption("env")        # 获取命令行的参数
    print("========env=====:", env)

    addopts = pytestconfig.getini("addopts")
    print("============addopts========:", addopts)
    base_url = pytestconfig.getini("base_url")  # 获取配置文件中的参数
    print("============base_url========:", base_url)

在这里插入图片描述

内置的其他一些fixture可以在👉🏻查看

  • 27
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值