【问题标题】:When to use pytest fixtures?何时使用 pytest 固定装置?
【发布时间】:2020-10-04 03:48:13
【问题描述】:

我是测试新手,我偶然发现了 pytest 固定装置,但我不完全确定何时使用它们以及它们为何有用。

例如,看下面的代码:

import pytest

@pytest.fixture
def input_value():
   input = 39
   return input

def test_divisible_by_3(input_value):
   assert input_value % 3 == 0

def test_divisible_by_6(input_value):
   assert input_value % 6 == 0

这里的pytest.fixture的作用是什么?为什么我们不能简单地创建一个名为input_value() 的函数并在测试函数中运行该函数?例如:

import pytest

def input_value():
   input = 39
   return input

def test_divisible_by_3():
   assert input_value() % 3 == 0

def test_divisible_by_6():
   assert input_value() % 6 == 0

为什么我们不能这样做?使用fixture有什么用?

【问题讨论】:

  • pytest 固定装置是设置/拆除测试用例或测试套件的通用方法,您的示例确实在固定装置和常规功能之间没有太大区别。你读过pytest fixtures: explicit, modular, scalable吗?这应该让您大致了解可以使用固定装置做什么。

标签: python-3.x unit-testing pytest decorator


【解决方案1】:

Pytest Fixtures 和常规函数都可用于构建测试代码并减少代码重复。 George Udosen 提供的答案很好地解释了这一点。

但是,OP 专门询问了 pytest.fixture 和常规 Python 函数之间的区别,并且存在许多区别:

Pytest Fixtures 的作用域

默认情况下,pytest.fixture 会为每个引用夹具的测试函数执行。但是,在某些情况下,夹具设置可能在计算上很昂贵或很耗时,例如初始化数据库。为此,pytest.fixture 可以配置为更大的scope。这允许 pytest.fixture 在模块中的测试(module 范围)甚至在 pytest 运行的所有测试(session 范围)中重用。以下示例使用模块范围的固定装置来加速测试:

from time import sleep
import pytest


@pytest.fixture(scope="module")
def expensive_setup():
    return sleep(10)

def test_a(expensive_setup):
    pass  # expensive_setup is instantiated for this test

def test_b(expensive_setup):
    pass  # Reuses expensive_setup, no need to wait 10s

虽然可以通过常规函数调用来实现不同的作用域,但作用域的固定装置使用起来更愉快。

Pytest Fixtures 基于依赖注入

Pytest 在测试收集阶段注册所有夹具。当测试函数需要一个名称与注册的夹具名称匹配的参数时,Pytest 将注意为测试实例化夹具并将实例提供给测试函数。这是dependency injection.的一种形式

与常规函数相比,优势在于您可以按名称引用任何pytest.fixture,而无需显式导入它。例如,Pytest 带有一个 tmp_path 固定装置,任何测试都可以使用它来处理临时文件。以下示例取自Pytest documentation

CONTENT = "content"


def test_create_file(tmp_path):
    d = tmp_path / "sub"
    d.mkdir()
    p = d / "hello.txt"
    p.write_text(CONTENT)
    assert p.read_text() == CONTENT
    assert len(list(tmp_path.iterdir())) == 1
    assert 0

用户在使用前不必导入tmp_path,非常方便。

甚至可以在没有测试功能请求的情况下将夹具应用于测试功能(请参阅Autouse fixtures)。

Pytest 固定装置可以参数化

很像test parametrization, fixture parametrization 允许用户指定夹具的多个“变体”,每个“变体”具有不同的返回值。使用该夹具的每个测试都将执行多次,每个变体一次。假设您想测试您的所有代码是否都针对 HTTP 和 HTTPS URL 进行了测试,您可能会执行以下操作:

import pytest


@pytest.fixture(params=["http", "https"])
def url_scheme(request):
    return request.param


def test_get_call_succeeds(url_scheme):
    # Make some assertions
    assert True

参数化的夹具将导致每个版本的夹具执行每个引用测试:

$ pytest
tests/test_fixture_param.py::test_get_call_succeeds[http] PASSED                                                                                                                                                                         [ 50%]
tests/test_fixture_param.py::test_get_call_succeeds[https] PASSED                                                                                                                                                                        [100%]

======== 2 passed in 0.01s ========

结论

与常规函数调用相比,Pytest 固定装置提供了许多生活质量改进。我建议始终更喜欢 Pytest 固定装置而不是常规函数,除非您必须能够直接调用固定装置。不直接调用pytest的fixtures,调用会失败。

【讨论】:

    【解决方案2】:

    我自己是 pytest 的新手,但我知道它减少了编写多个测试多次使用的代码的需要,因为在这种情况下,您需要多次重写该函数,而这个 sn-p 将是一个启动器:

    Fixtures 用于将一些数据提供给测试,例如数据库 连接、要测试的 URL 和某种输入数据。

    因此,我们不必为每个测试运行相同的代码,而是可以 将夹具功能附加到测试,它将运行并返回 在执行每个测试之前将数据添加到测试中。

    -- 来源:https://www.tutorialspoint.com/pytest/pytest_fixtures.htm

    通常,它有助于在您的测试之间共享它们共同的资源,并大大减少重复。同样,这些夹具函数的返回值可以作为“输入参数”传递到各个测试中,如下所示:

    @pytest.fixture
    def input_value():
       input = 39
       return input
    
    def test_divisible_by_3(input_value):
       assert input_value % 3 == 0
    

    【讨论】:

    • 我们还可以在单​​独的模块中编写一次固定装置,然后在任何地方重复使用它们而无需导入,这是一件好事,因为我们将参数及其创建与测试场景和测试的 API 分开
    • 我需要多次重写该函数是什么意思?我只需要编写一次,然后在测试函数中调用它?
    • 这就是我试图让你不需要对灯具这样做的一点!
    • 但是为什么要使用 pytest 夹具呢?为什么我们不能只定义一个与 pytest 夹具具有相同返回值的普通函数?
    • 与@theman 相同的问题,为什么?没有@pytest.fixture,一个普通的函数可以做,也可以共享重用。我四处搜索,但仍然很少有人解释为什么不使用普通函数来共享数据输入。
    猜你喜欢
    • 2018-02-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-02-08
    • 2017-01-28
    相关资源
    最近更新 更多