Pytest框架中fixture功能详解

文章目录

1 定义 Fixture函数

2 Fixture 的函数参数

2.1 传入其他fixture函数作为参数

2.2 传入request对象参数

示例1:访问fixture的调用者

示例2:使用fixture的参数

3 Fixture 的作用域参数scope

3.1 scope=class场景

3.2 scope=session场景

4 Fixture 的自动使用参数autouse=True

5 Fixture 的参数化params和ids

6 fixture函数传入的request到底是啥?

pytest 的 fixture 是一个非常强大的功能,它允许你在测试函数或测试类之间共享设置和清理代码。fixture可以被看作是测试中的"依赖注入"机制,它允许你以函数的形式定义这些依赖,并在需要时将其注入到测试函数中。

pytest官方文档是这样解释的:

https://docs.pytest.org/en/latest/how-to/fixtures.html#how-to-fixtures

由于英语不好,只能百度翻译如下:

|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 在测试中,fixtures(夹具)为测试提供了一个定义明确、可靠且一致的上下文。这个上下文可以包括环境(例如配置有已知参数的数据库)或内容(如数据集)。 Fixtures 定义了测试的 "arrange"(安排)阶段所需的步骤和数据。在 pytest 中,fixtures 是你定义的函数,用于这个目的。它们也可以用于定义测试的 "act"(行动)阶段,这是设计更复杂测试的一个强大技巧。 由 fixtures 设置的服务、状态或其他操作环境通过参数被测试函数访问。对于测试函数使用的每个 fixture,通常在测试函数的定义中有一个以 fixture 命名的参数。 |

我们可以通过 @pytest.fixture 装饰器来告诉 pytest 某个函数是一个 fixture。

我们举个简单的例子理解下:

定义一个用 @pytest.fixture 装饰的函数fixture_steup_exec,

并将函数fixture_steup_exec作为参数传入测试用例test_1中。

@pytest.fixture
def fixture_steup_exec():
  print('\n执行测试用例前,先执行我这个fixture函数')

def test_1(fixture_steup_exec):
  assert 1==1
  print('\n用例test_1执行成功'

当我们执行测试用例test_1时,程序发现参数fixture_steup_exec正是被 @pytest.fixture装饰的函数,那么先执行被装饰函数中的代码,然后才执行测试用例的代码。

test_1的执行结果如下:

1 定义 Fixture

使用 @pytest.fixture 装饰器来定义一个 fixture。

主要有几个参数fixture_function,scope,params,autouse,ids等,下面几个章节会详细介绍。

被@pytest.fixture 装饰的函数会在测试函数或测试方法之前运行,并返回一个值,该值可以作为参数传递给测试函数或测试方法。

举例:定义my_fixture函数返回data,将该函数传入测试用例test_example。

@pytest.fixture
def my_fixture():
  #这里是设置代码 
  data = "Hello, pytest!"
  return data
def test_example(my_fixture):
  assert my_fixture=="Hello, pytest!"
  print(f"\nmy_fixture 返回的值: {my_fixture}")

用例执行结果:

2 Fixture 的fixture_function参数

1)Fixture 函数可以接受其他 fixture 作为参数。换句话说定义的fixture 可以依赖于其他 fixture 来执行设置和清理工作。

2)Fixture 函数还可以接受 request 对象作为参数,这个对象提供了关于当前请求的额外信息,如 fixture 的名称、参数等。

2.1 传入其他fixture函数作为参数

一个 fixture 可以依赖于另一个 fixture,只需将依赖的 fixture 作为参数传递给 fixture 函数即可。

举例:定义fixture_a,同时定义fixture_b,参数传入fixture_a。

@pytest.fixture
def fixture_a():
  return "a"

@pytest.fixture
def fixture_b(fixture_a): # 依赖于 fixture_a
  return fixture_a + "b"

def test_both_fixtures(fixture_b):
  assert fixture_b == "ab"
  print('\n调用了fixture_b 和 fixture_a')

将fixture_b作为参数传入测试用例test_both_fixtures,执行成功。

2.2 传入request对象参数

request是pytest中fixture函数的一个特殊参数,它提供了许多有关当前测试请求的信息。request对象主要有以下功能:

1)访问fixture的调用者:我们可以知道是哪个测试或fixture调用了当前的fixture。

2)获取fixture的参数:如果fixture有参数可以使用request.param来访问这些参数。

3)访问fixture的上下文:request对象还提供了许多其他方法和属性,允许你更深入地了解当前测试请求的上下文。

以下是一些使用request的示例:

示例1:访问fixture的调用者

通过在fixture函数传入request,通过request.node.name获取测试用例名称。

@pytest.fixture
def my_parametrized_fixture(request):
  print(f'是那个测试用例调用了我: {request.node.name}')

def test_case1(my_parametrized_fixture):
  print(f"Running testcase1")

示例2:使用fixture的参数

举例:传入两个参数value1和value2,并打印下测试用例获取的fixture的参数值。

@pytest.fixture(params=["value1", "value2"])
def example_fixture(request):
  return request.param

def test_example(example_fixture):
  print(f"Using value: {example_fixture}")

用例执行结果:

3 Fixture 的作用域参数scope

默认情况下,fixture 的作用域是函数级,可通过 scope 参数来改变 fixture 的作用域。可用的作用域有:["session", "package", "module", "class", "function"]

scope 参数解释:

  • function(默认)

每个测试函数都会执行一次 fixture。

适用于每个测试用例都需要独立设置和清理资源的场景。

  • class

每个测试类都会执行一次 fixture。

在类中定义的第一个测试方法执行前执行 fixture,在类中的所有测试方法执行后执行 teardown(如果使用了 yield)。

适用于类中的多个测试方法共享相同设置和清理资源的场景。

  • module

一个 Python 模块(一个 .py 文件)只会执行一次 fixture。

在模块中的第一个测试函数或方法执行前执行 fixture,在模块中的所有测试函数或方法执行后执行 teardown(如果使用了 yield)。

适用于整个模块内的多个测试函数或方法共享相同设置和清理资源的场景。

  • session

整个测试会话期间只执行一次 fixture。

在 pytest 会话开始时执行 fixture,在所有测试文件执行完毕后执行 teardown(如果使用了 yield)。

适用于整个测试套件需要共享的设置和清理资源的场景,如数据库连接、浏览器驱动等。

当 fixture 设置为 autouse=True 时,结合 scope 参数,fixture 将在指定的作用域内自动生效,无需在每个测试函数或类中显式引用。

3.1 scope=class场景

举例:在测试类执行前执行setup和teardown的动作。

定义一个fixture函数,在该函数使用 @pytest.fixture(scope='class') 装饰器(应用于类时scope参数定义为class),并使用yield关键字,yield上面的代码在类中第一个测试用例执行前执行,yield下面的代码在类中最后一条用例执行后执行。我们将这个fixture 作为类中测试方法的参数,以此来实现类中的setup和teardown功能。

@pytest.fixture(scope='class')
def class_setup_teardown():
  print("\nClass setup (equal to setup_class)")
  # 设置代码
  yield
  print("\nClass teardown (equal to teardown_class)")
  # 清理代码

第一种方式:在测试函数中传入定义的fixture函数作为参数
class Testcase:
  def test_one(self, class_setup_teardown): # 直接将fixture作为参数
    print("\nRunning test_one")
    # 测试代码
  def test_two(self, class_setup_teardown): # 同样地,直接作为参数
    print("\nRunning test_two")
    # 测试代码
  • 第二种方式:在测试类中注明使用fixture,测试函数中不传参数

    @pytest.mark.usefixtures("class_setup_teardown")
    class Testcase:
    def test_one(self):
    print("\nRunning test_one")
    # 测试代码
    def test_two(self):
    print("\nRunning test_two")
    # 测试代码

用例执行后结果如下:两种方式都实现了测试类setup和teardown的功能

3.2 scope=session场景

由于 session 级别的 fixture 在整个测试会话中只执行一次,因此它可以跨多个测试文件共享资源(而module级别的只能在单个py文件内适用)。

举例:我们有一个需要连接数据库的 fixture,并且这个连接在整个测试会话期间只需要建立一次。

@pytest.fixture(scope='session')
def db_connection_fixture():
  # 这里是建立数据库连接的代码
  print("Connecting to database...")
  yield # 暂停点,测试函数开始执行
  # 这里是关闭数据库连接的代码
  print("Closing database connection...")
  # 之后的测试函数或类将自动使用这个 fixture,无需显式引用

  def test_database_operation(db_connection_fixture):
    # 在这里执行数据库操作,db_connection fixture 已经在会话开始时自动建立连接
    pass

由于 scope="session" ,db_connection fixture 将在整个测试会话开始时自动建立数据库连接,并在所有测试执行完毕后自动关闭连接。

4 Fixture 的自动使用参数autouse=True

autouse=True 参数使 fixture 在所有测试函数或测试类中自动使用,而无需将其作为参数传递。但这可能会导致代码更难理解和维护。

举例:上面我们创建的scope=class的fixture函数,如果加上参数autouse=True,则不需要在测试类中在使用这个fixture函数。

@pytest.fixture(scope='session', autouse=True)
def class_setup_teardown():
  print("\nClass setup (equal to setup_class)")
  # 设置代码
  yield
  print("\nClass teardown (equal to teardown_class)")
  # 清理代码

#@pytest.mark.usefixtures("class_setup_teardown")
#完全可以注释该fixture函数的使用
class Testcase2:
  def test_one(self):
    print("\nRunning test_one")
    # 测试代码
  def test_two(self):
    print("\nRunning test_two")
    # 测试代码

5 Fixture 的参数化params和ids

允许为fixture指定多个参数值,并为每个参数值运行测试函数。通常params与ids参数结合使用,ids为参数化值提供别名,在后续测试报告中更容易识别每个参数化测试。

举例:给fixture传入三个参数和它们的ids,执行测试用例后,其实会执行3个用例。

@pytest.fixture(params=[1, 2, 3],ids=['id1','id2','id3'])
def my_parametrized_fixture(request):
  return request.param

def test_parametrized_fixture(my_parametrized_fixture):
  print(f"Running test with {my_parametrized_fixture}")

执行后结果如下:

6 fixture函数传入的request到底是啥?

request 参数,对应访问的是特殊的 FixtureRequest 对象,它提供了关于当前请求的上下文信息。

以下是位于pytest.fixture文件中的FixtureRequest 类:

在初始化函数中添加了一行打印,如上图所示。

FixtureRequest 对象包含以下一些有用的属性和方法:

属性

1)fixturename: 当前 fixture 的名称。

2)param: 如果 fixture 被参数化,则这是当前参数的值。

3)scope: fixture 的作用域(例如 "function", "class", "module", "session")。

4) config: pytest 的配置对象。

5) session: pytest 的 session 对象。

6)node: 收集到的节点(通常是测试用例或类)。

7)function: 如果 fixture 被一个测试函数调用,则这是该函数。

8)cls: 如果 fixture 被一个测试类使用,则这是该类。

9)instance: 如果 fixture 被一个测试类的实例方法使用,则这是该实例。

10)module: 如果 fixture 被一个模块中的测试使用,则这是该模块。

11)fspath: 调用 fixture 的文件路径。

方法

1)addfinalizer(finalizer): 添加一个将在测试结束后调用的 finalizer 函数。这可以用于执行清理操作。

2)getfixturevalue(argname): 请求另一个 fixture 的值。

3)getparam(name, default): 检索参数的当前值,如果该参数未定义则返回默认值。

举例:在测试用例中打印下部分request的属性:

@pytest.fixture(params=["value1", "value2"])
def example_fixture(request):
  return request

def test_example(example_fixture):
  print(f"\nrequest.fspath : {example_fixture.fspath}")
  print(f"\nrequest.node : {example_fixture.node}")
  print(f"\nrequest.scope : {example_fixture.scope}")
  print(f"\nrequest.fixturename : {example_fixture.fixturename}")
  print(f"\nrequest.module : {example_fixture.module}")
  print(f"\nrequest.config : {example_fixture.config}")

用例执行后结果:打印了上面自己添加的一行记录,说明调用了FixtureRequest 。

共勉: 东汉·班固《汉书·枚乘传》:"泰山之管穿石,单极之绠断干。水非石之钻,索非木之锯,渐靡使之然也。"

-----指水滴不断地滴,可以滴穿石头;

-----比喻坚持不懈,集细微的力量也能成就难能的功劳。

----感谢读者的阅读和学习,点个赞和关注吧,谢谢大家。

相关推荐
VX_DZbishe1 小时前
springboot旅游管理系统-计算机毕业设计源码16021
java·spring boot·python·servlet·django·flask·php
青春之我_XP1 小时前
【知识图谱系列】Neo4j使用Py2neo与python进行链接
python·知识图谱·neo4j
IT数据小能手1 小时前
Python中爬虫编程的常见问题及解决方案
开发语言·爬虫·python
橙子味冰可乐1 小时前
isprintable()方法——判断字符是否为可打印字符
java·前端·javascript·数据库·python
Hi202402171 小时前
将数据切分成N份,采用NCCL异步通信,让all_gather+matmul尽量Overlap
pytorch·python·性能优化·分布式训练·nccl·融合算子
ck_RFID_3 小时前
RFID技术在粉末涂料配料生产线的精准应用
信息可视化·自动化·rfid·工业制造
凉拌糖醋鱼4 小时前
Python-PDF文件密码破解小工具
开发语言·python·pdf·密码破解
IT数据小能手4 小时前
天猫商品列表数据接口(Tmall.item_search)
大数据·爬虫·python
逢生博客6 小时前
Transformers 安装与基本使用
pytorch·python·语言模型·tensorflow·transformer
阿米诺s6 小时前
python本学期所有代码!
开发语言·爬虫·python