pytest8.x版本 中文使用文档-------4-3.如何使用夹具(Fixtures)

目录

参数化夹具

使用pytest.param()为参数化fixture添加标记

模块化:在fixture函数中使用fixtures

通过fixture实例自动分组测试

在类和模块中使用 夹具通过usefixtures 装饰器

在不同层级上覆盖 fixture

在文件夹(conftest)级别覆盖 fixture

在测试模块级别覆盖一个fixture

直接通过测试参数化来“覆盖”一个fixture

使用非参数化的fixture覆盖参数化的fixture,反之亦然

使用来自其他项目的fixture


参数化夹具

夹具函数可以被参数化,在这种情况下,它们将被多次调用,每次调用都会执行一组依赖于该夹具的测试,即那些依赖于此夹具的测试。测试函数通常不需要意识到它们正在重新运行。夹具的参数化有助于为那些本身可以以多种方式配置的组件编写详尽的功能测试。

在 pytest 中,参数化夹具允许你定义一组不同的参数值,并且为每个参数值运行一次夹具函数及其依赖的测试。这样,你可以为不同的输入条件测试你的代码,而无需为每个条件编写单独的测试函数。这对于测试具有多个可能配置或状态的组件特别有用。

扩展前面的例子,我们可以标记夹具以创建两个smtp_connection夹具实例,这将导致所有使用该夹具的测试都运行两次。夹具函数通过特殊的request对象访问每个参数:

# conftest.py 文件的内容  
import smtplib  
  
import pytest  
  
@pytest.fixture(scope="module", params=["smtp.gmail.com", "mail.python.org"])  
def smtp_connection(request):  
    smtp_connection = smtplib.SMTP(request.param, 587, timeout=5)  
    yield smtp_connection  
    print(f"finalizing {smtp_connection}")  
    smtp_connection.close()

smtp_connection夹具被标记为pytest.fixture,并且使用了params参数来指定两个SMTP服务器地址。scope="module"表示这个夹具在每个测试模块中只会被实例化一次,但是在这个上下文中,由于params的使用,它实际上会为每个参数值实例化一次,但仍然是模块级别的,这意味着在单个模块内所有使用此夹具的测试都会针对每个参数值运行一次。在smtp_connection函数内部,request.param用于访问当前测试正在使用的参数值(即SMTP服务器地址)。

主要的变化是在使用@pytest.fixture声明时添加了params参数,它是一个值列表,对于列表中的每个值,夹具函数都会执行一次,并且可以通过request.param访问当前的值。测试函数的代码不需要进行任何更改。我们再运行一次:

$ pytest -q test_module.py
FFFF                                                                 [100%]
================================= FAILURES =================================
________________________ test_ehlo[smtp.gmail.com] _________________________

smtp_connection = <smtplib.SMTP object at 0xdeadbeef0004>

    def test_ehlo(smtp_connection):
        response, msg = smtp_connection.ehlo()
        assert response == 250
        assert b"smtp.gmail.com" in msg
>       assert 0  # for demo purposes
E       assert 0

test_module.py:7: AssertionError
________________________ test_noop[smtp.gmail.com] _________________________

smtp_connection = <smtplib.SMTP object at 0xdeadbeef0004>

    def test_noop(smtp_connection):
        response, msg = smtp_connection.noop()
        assert response == 250
>       assert 0  # for demo purposes
E       assert 0

test_module.py:13: AssertionError
________________________ test_ehlo[mail.python.org] ________________________

smtp_connection = <smtplib.SMTP object at 0xdeadbeef0005>

    def test_ehlo(smtp_connection):
        response, msg = smtp_connection.ehlo()
        assert response == 250
>       assert b"smtp.gmail.com" in msg
E       AssertionError: assert b'smtp.gmail.com' in b'mail.python.org\nPIPELINING\nSIZE 51200000\nETRN\nSTARTTLS\nAUTH DIGEST-MD5 NTLM CRAM-MD5\nENHANCEDSTATUSCODES\n8BITMIME\nDSN\nSMTPUTF8\nCHUNKING'

test_module.py:6: AssertionError
-------------------------- Captured stdout setup ---------------------------
finalizing <smtplib.SMTP object at 0xdeadbeef0004>
________________________ test_noop[mail.python.org] ________________________

smtp_connection = <smtplib.SMTP object at 0xdeadbeef0005>

    def test_noop(smtp_connection):
        response, msg = smtp_connection.noop()
        assert response == 250
>       assert 0  # for demo purposes
E       assert 0

test_module.py:13: AssertionError
------------------------- Captured stdout teardown -------------------------
finalizing <smtplib.SMTP object at 0xdeadbeef0005>
========================= short test summary info ==========================
FAILED test_module.py::test_ehlo[smtp.gmail.com] - assert 0
FAILED test_module.py::test_noop[smtp.gmail.com] - assert 0
FAILED test_module.py::test_ehlo[mail.python.org] - AssertionError: asser...
FAILED test_module.py::test_noop[mail.python.org] - assert 0
4 failed in 0.12s

我们看到,我们的两个测试函数各自针对不同的smtp_connection实例运行了两次。还需要注意的是,在使用mail.python.org连接时,第二个测试在test_ehlo中失败了,因为预期的服务器字符串与收到的不同。

pytest会为参数化夹具中的每个夹具值构建一个字符串作为测试ID,例如上述例子中的test_ehlo[smtp.gmail.com]test_ehlo[mail.python.org]。这些ID可以与-k选项一起使用来选择要运行的特定案例,并且在某个案例失败时,它们还可以标识出具体的失败案例。运行pytest时加上--collect-only选项将显示生成的ID。

在pytest中,测试ID用于唯一标识测试用例或测试参数化中的每个实例。对于数字、字符串、布尔值和None,pytest会使用它们通常的字符串表示形式作为测试ID。对于其他对象,pytest会根据参数名生成一个字符串。不过你可以通过使用ids关键字参数,可以自定义测试ID中使用的字符串,用于特定的fixture值:

以下示例展示了如何使用ids来自定义测试ID:

# test_ids.py 文件内容  
import pytest  
  
# 使用 ids 列表直接指定测试ID  
@pytest.fixture(params=[0, 1], ids=["spam", "ham"])  
def a(request):  
    return request.param  
  
def test_a(a):  
    pass  
  
# 使用 ids 函数动态生成测试ID  
def idfn(fixture_value):  
    if fixture_value == 0:  
        return "eggs"  
    else:  
        return None  # 如果函数返回 None,pytest 将使用自动生成的ID  
  
@pytest.fixture(params=[0, 1], ids=idfn)  
def b(request):  
    return request.param  
  
def test_b(b):  
    pass

上述内容展示了如何使用ids,它既可以是一个要使用的字符串列表,也可以是一个函数,该函数将使用fixture的值作为参数被调用,并需要返回一个字符串来使用。在后一种情况下,如果函数返回None,则pytest将使用自动生成的ID。这种方式提供了灵活性,允许开发者根据具体情况选择最适合的方式来标识测试ID。

运行上述测试将使用以下测试ID:

$ pytest --collect-only
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y
rootdir: /home/sweet/project
collected 12 items

<Dir fixtures.rst-219>
  <Module test_anothersmtp.py>
    <Function test_showhelo[smtp.gmail.com]>
    <Function test_showhelo[mail.python.org]>
  <Module test_emaillib.py>
    <Function test_email_received>
  <Module test_finalizers.py>
    <Function test_bar>
  <Module test_ids.py>
    <Function test_a[spam]>
    <Function test_a[ham]>
    <Function test_b[eggs]>
    <Function test_b[1]>
  <Module test_module.py>
    <Function test_ehlo[smtp.gmail.com]>
    <Function test_noop[smtp.gmail.com]>
    <Function test_ehlo[mail.python.org]>
    <Function test_noop[mail.python.org]>

======================= 12 tests collected in 0.12s ========================

使用pytest.param()为参数化fixture添加标记

pytest.param()可以在参数化fixture的值集中应用标记,其方式与在@pytest.mark.parametrize中使用它们相同。这意味着你可以对参数化fixture中的特定值或组合进行标记,以便在测试运行时应用特定的行为或条件。

例子:

# content of test_fixture_marks.py
import pytest


@pytest.fixture(params=[0, 1, pytest.param(2, marks=pytest.mark.skip)])
def data_set(request):
    return request.param


def test_data(data_set):
    pass

运行此测试将跳过值为2的data_set调用:

$ pytest test_fixture_marks.py -v
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y -- $PYTHON_PREFIX/bin/python
cachedir: .pytest_cache
rootdir: /home/sweet/project
collecting ... collected 3 items

test_fixture_marks.py::test_data[0] PASSED                           [ 33%]
test_fixture_marks.py::test_data[1] PASSED                           [ 66%]
test_fixture_marks.py::test_data[2] SKIPPED (unconditional skip)     [100%]

======================= 2 passed, 1 skipped in 0.12s =======================

再比如:

import pytest  
  
# 使用 pytest.param() 为参数化fixture中的值添加标记  
@pytest.fixture(params=[  
    pytest.param(1, marks=pytest.mark.basic),  
    pytest.param(2, marks=pytest.mark.basic),  
    pytest.param(3, marks=pytest.mark.advanced),  
    pytest.param(4, marks=[pytest.mark.xfail, pytest.mark.advanced])  
])  
def input_value(request):  
    return request.param  
  
def test_example(input_value):  
    # 测试逻辑  
    if input_value == 4:  
        # 这个测试预期会失败  
        assert False  
    else:  
        # 其他测试逻辑  
        assert input_value >= 1  
  
# 在命令行中运行测试时,你可以使用 -m 选项来只运行具有特定标记的测试  
# 例如,只运行标记为 basic 的测试:pytest -m basic  
# 或者,只运行没有 xfail 标记的测试(这通常意味着排除预期失败的测试)  
# pytest -m 'not xfail'

模块化:在fixture函数中使用fixtures

除了在测试函数中使用fixtures之外,fixture函数本身也可以使用其他fixtures。这有助于实现fixtures的模块化设计,并允许跨多个项目重用特定于框架的fixtures。

这样做的好处是,你可以构建一个复杂的fixture结构,每个fixture都专注于其特定的任务或资源,而它们之间则通过依赖关系相互连接。这样,当你需要在多个测试或项目中重复使用某些复杂的配置或资源时,你可以简单地重用这些fixtures,而无需在每个地方都重新编写相同的设置代码。

例如,如果你有一个用于数据库连接的fixture,你可以在多个fixture中重用它,这些fixture分别用于设置不同的测试数据或执行不同的数据库操作,而无需在每个fixture中都重新建立数据库连接。

作为一个简单的例子,我们可以扩展前面的示例,并实例化一个对象app,其中我们将已定义的smtp_connection资源嵌入其中:

# test_appsetup.py 文件的内容  
  
import pytest  
  
# 定义了一个App类,它接受一个smtp_connection参数并在初始化时设置它  
class App:  
    def __init__(self, smtp_connection):  
        self.smtp_connection = smtp_connection  
  
# 使用pytest.fixture装饰器定义了一个名为app的fixture,其作用域为module级别  
# 它接收之前定义的smtp_connection fixture,并使用它来实例化App对象  
@pytest.fixture(scope="module")  
def app(smtp_connection):  
    return App(smtp_connection)  
  
# 定义了一个测试函数,它接收app fixture作为参数  
# 该测试函数检查app对象的smtp_connection属性是否存在(即非空或非None)  
def test_smtp_connection_exists(app):  
    assert app.smtp_connection  
  

这里我们声明了一个app fixture,它接收之前定义的smtp_connection fixture,并用它来实例化一个App对象。  
让我们运行它:  


$ pytest -v test_appsetup.py  
=========================== test session starts ============================  
platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y -- $PYTHON_PREFIX/bin/python  
cachedir: .pytest_cache  
rootdir: /home/sweet/project  
collecting ... collected 2 items  # 表示收集到了2个测试项  
  
test_appsetup.py::test_smtp_connection_exists[smtp.gmail.com] PASSED [ 50%]  # 第一个测试项通过  
test_appsetup.py::test_smtp_connection_exists[mail.python.org] PASSED [100%]  # 第二个测试项也通过  
  
============================ 2 passed in 0.12s =============================  # 2个测试项都通过了,总共耗时0.12秒

由于smtp_connection被参数化了,测试将使用两个不同的App实例和相应的SMTP服务器运行两次。app fixture无需了解smtp_connection的参数化,因为pytest会完全分析fixture依赖图。

请注意,app fixture的作用域是模块级别的,并且它使用了一个模块级别的smtp_connection fixture。即使smtp_connection是在会话作用域内缓存的,这个示例仍然可以工作:fixture使用“更广泛”作用域的fixture是可以的,但反过来则不行:一个会话作用域的fixture不能以一种有意义的方式使用模块作用域的fixture。这是因为作用域较小的fixture(如模块级别)的生命周期和作用范围被包含在作用域更大的fixture(如会话级别)之内,但反过来则会导致作用域和生命周期的冲突。

通过fixture实例自动分组测试

pytest在测试运行时尽量减少活动fixture的数量。如果你有一个参数化的fixture,那么所有使用它的测试将首先使用一个实例执行,然后在下一个fixture实例创建之前调用终结器(finalizers)。这样做的好处之一是简化了那些创建和使用全局状态的应用程序的测试。通过这种方式,每个fixture实例的上下文都是独立的,有助于避免不同测试之间的相互影响,确保测试的独立性和准确性。

以下示例使用了两个参数化的fixture,其中一个fixture的作用域是基于每个模块的,并且所有函数都执行print打印调用来显示设置/拆除(setup/teardown)流程:

# test_module.py 文件内容  
import pytest  
  
# 这是一个模块级别的参数化fixture,它将为每个模块参数("mod1" 和 "mod2")执行一次设置和一次拆除  
@pytest.fixture(scope="module", params=["mod1", "mod2"])  
def modarg(request):  
    param = request.param  
    print("  SETUP modarg", param)  # 设置时打印  
    yield param  # 产出参数,以便测试函数可以使用  
    print("  TEARDOWN modarg", param)  # 拆除时打印  
  
# 这是一个函数级别的参数化fixture,它将为每个测试函数分别针对每个参数(1 和 2)执行设置和拆除  
@pytest.fixture(scope="function", params=[1, 2])  
def otherarg(request):  
    param = request.param  
    print("  SETUP otherarg", param)  # 设置时打印  
    yield param  # 产出参数,以便测试函数可以使用  
    print("  TEARDOWN otherarg", param)  # 拆除时打印  
  
# 这个测试函数只使用了otherarg fixture  
def test_0(otherarg):  
    print("  RUN test0 with otherarg", otherarg)  
  
# 这个测试函数只使用了modarg fixture  
def test_1(modarg):  
    print("  RUN test1 with modarg", modarg)  
  
# 这个测试函数同时使用了otherarg和modarg两个fixture  
# 注意:由于modarg是模块级别的,而otherarg是函数级别的,因此对于modarg的每个参数,otherarg的每个参数都会被测试一次  
def test_2(otherarg, modarg):  
    print(f"  RUN test2 with otherarg {otherarg} and modarg {modarg}")

让我们以详细模式运行测试,并查看打印输出:

$ pytest -v -s test_module.py  
=========================== test session starts ============================  
platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y -- $PYTHON_PREFIX/bin/python  
cachedir: .pytest_cache  
rootdir: /home/sweet/project  
collecting ... collected 8 items  
  
test_module.py::test_0[1]   SETUP otherarg 1  
  RUN test0 with otherarg 1  
PASSED  TEARDOWN otherarg 1  
  
test_module.py::test_0[2]   SETUP otherarg 2  
  RUN test0 with otherarg 2  
PASSED  TEARDOWN otherarg 2  
  
test_module.py::test_1[mod1]   SETUP modarg mod1  
  RUN test1 with modarg mod1  
PASSED  
test_module.py::test_2[mod1-1]   SETUP otherarg 1  
  RUN test2 with otherarg 1 and modarg mod1  
PASSED  TEARDOWN otherarg 1  
  
test_module.py::test_2[mod1-2]   SETUP otherarg 2  
  RUN test2 with otherarg 2 and modarg mod1  
PASSED  TEARDOWN otherarg 2  
  
test_module.py::test_1[mod2]   TEARDOWN modarg mod1  
  SETUP modarg mod2  
  RUN test1 with modarg mod2  
PASSED  
test_module.py::test_2[mod2-1]   SETUP otherarg 1  
  RUN test2 with otherarg 1 and modarg mod2  
PASSED  TEARDOWN otherarg 1  
  
test_module.py::test_2[mod2-2]   SETUP otherarg 2  
  RUN test2 with otherarg 2 and modarg mod2  
PASSED  TEARDOWN otherarg 2  
  TEARDOWN modarg mod2  
  
============================ 8 passed in 0.12s =============================

可以看到,参数化的模块级作用域modarg资源导致了测试执行的顺序,使得“活跃”资源数量尽可能少。在mod2资源设置之前,mod1参数化资源的终结器就已经执行了。

特别注意的是,test_0是完全独立的,并且首先完成。然后依次是:test_1使用mod1执行,test_2使用mod1执行,test_1使用mod2执行,最后test_2使用mod2执行。

而函数作用域的otherarg参数化资源在每个使用它的测试之前被设置,并在每个测试之后被拆除。

在类和模块中使用 夹具通过usefixtures 装饰器

有时测试函数不需要直接访问一个 fixture 对象。例如,测试可能需要将当前工作目录设置为一个空目录,但除此之外并不关心这个具体是哪个目录。下面是如何使用标准的 tempfile 和 pytest fixtures 来实现这一点的示例。我们将 fixture 的创建过程分离到一个 conftest.py 文件中(conftest.py 文件是 pytest 特有的,它允许你定义作用于整个测试目录的 fixtures 和钩子(hooks)。当你将 fixture 定义在 conftest.py 文件中时,它会自动被该目录下所有的测试文件所识别和使用):

#content of conftest.py

import os  
import tempfile  
  
import pytest  
  
@pytest.fixture  
def cleandir():  
    """  
    使用临时目录作为当前工作目录,并在测试完成后恢复原始工作目录。  
    """  
    with tempfile.TemporaryDirectory() as newpath:  
        # 保存当前工作目录  
        old_cwd = os.getcwd()  
        # 切换到临时目录  
        os.chdir(newpath)  
        # yield 之后,测试函数开始执行  
        yield  
        # 测试函数执行完毕后,恢复原始工作目录  
        os.chdir(old_cwd)

并通过在测试模块中使用 usefixtures 标记来声明其使用:

# content of test_setenv.py
import os  
  
import pytest  
  
# 使用 usefixtures 标记来声明 TestDirectoryInit 类中的每个测试方法都需要 cleandir fixture  
@pytest.mark.usefixtures("cleandir")  
class TestDirectoryInit:  
    def test_cwd_starts_empty(self):  
        """  
        测试当前工作目录在开始时是空的。  
        """  
        assert os.listdir(os.getcwd()) == []  # 确保当前目录为空  
        # 在当前目录创建一个文件  
        with open("myfile", "w", encoding="utf-8") as f:  
            f.write("hello")  
        # 注意:虽然这里创建了文件,但由于 yield 的作用,文件将在测试结束后自动被删除(因为临时目录被删除了)  
  
    def test_cwd_again_starts_empty(self):  
        """  
        测试当前工作目录在每个测试方法开始前都是空的。  
        """  
        assert os.listdir(os.getcwd()) == []  # 再次确保当前目录为空

由于使用了 usefixtures 标记,cleandir fixture 将在执行 TestDirectoryInit 类中的每个测试方法时自动被调用,就好像你为它们每个都指定了一个名为 cleandir 的函数参数一样。现在,让我们运行这些测试来验证我们的 fixture 是否被激活,并且测试是否通过:

$ pytest -q
..                                                                   [100%]
2 passed in 0.12s

你可以像这样指定多个 fixture:

@pytest.mark.usefixtures("cleandir", "anotherfixture")  
def test():  
    # ... 测试代码 ...

此外,你也可以在测试模块级别使用 pytestmark来指定 fixture 的使用:

pytestmark = pytest.mark.usefixtures("cleandir")  
  
# 然后在这个模块中的测试函数或类将自动使用 "cleandir" fixture

还有一种可能的方式是将你项目中所有测试所需的 fixture 放入一个 ini 文件中:

# content of pytest.ini
[pytest]
usefixtures = cleandir

警告

请注意,这个标记在 fixture 函数中没有任何效果。例如,下面的用法不会按预期工作:

@pytest.mark.usefixtures("my_other_fixture")  
@pytest.fixture  
def my_fixture_that_sadly_wont_use_my_other_fixture():  
    # ...

上述代码会产生一个弃用警告,并且在 Pytest 8 中将成为一个错误。这是因为 @pytest.mark.usefixtures 标记是设计用来在测试函数或测试类上使用的,而不是在 fixture 函数上。

在不同层级上覆盖 fixture

在相对较大的测试套件中,你很可能需要用局部定义的 fixture 来覆盖全局或根级的 fixture,以保持测试代码的可读性和可维护性。这样做可以让你为特定的测试场景或测试集定制环境或前置条件,而不影响其他测试。例如,你可能有一个全局的 database_fixture,但在某些测试模块或测试类中,你可能需要一个不同的数据库配置或初始化方式,这时就可以通过定义同名的局部 fixture 来覆盖全局的 fixture。Pytest 会根据 fixture 的作用域和查找顺序来决定使用哪个 fixture。

在文件夹(conftest)级别覆盖 fixture

给定以下测试文件结构:

tests/
    conftest.py
        # content of tests/conftest.py
        import pytest

        @pytest.fixture
        def username():
            return 'username'

    test_something.py
        # content of tests/test_something.py
        def test_username(username):
            assert username == 'username'

    subfolder/
        conftest.py
            # content of tests/subfolder/conftest.py
            import pytest

            @pytest.fixture
            def username(username):
                return 'overridden-' + username

        test_something_else.py
            # content of tests/subfolder/test_something_else.py
            def test_username(username):
                assert username == 'overridden-username'

正如你所看到的,对于特定的测试文件夹层级,可以使用相同名称的 fixture 来进行覆盖。值得注意的是,从覆盖的 fixture 中可以很容易地访问到基础或超类 fixture,这在上面的示例中已经得到了应用。

在测试模块级别覆盖一个fixture

对于特定的测试模块(即Python文件),你可以定义一个与全局或父级conftest.py文件中定义的fixture同名的fixture,从而在该测试模块内部使用新的fixture定义。Pytest会按照作用域(scope)和查找顺序(通常是最近的定义优先)来选择使用哪个fixture。

当你在测试模块级别覆盖fixture时,你只需在该测试模块的顶层代码中(即直接在该Python文件内)使用@pytest.fixture装饰器来定义一个新的fixture。这个新的fixture将仅在该测试模块内有效,并会覆盖任何具有相同名称的、作用域更广的fixture。

tests/
    conftest.py
        # content of tests/conftest.py
        import pytest

        @pytest.fixture
        def username():
            return 'username'

    test_something.py
        # content of tests/test_something.py
        import pytest

        @pytest.fixture
        def username(username):
            return 'overridden-' + username

        def test_username(username):
            assert username == 'overridden-username'

    test_something_else.py
        # content of tests/test_something_else.py
        import pytest

        @pytest.fixture
        def username(username):
            return 'overridden-else-' + username

        def test_username(username):
            assert username == 'overridden-else-username'

直接通过测试参数化来“覆盖”一个fixture

tests/
    conftest.py
        # content of tests/conftest.py
        import pytest

        @pytest.fixture
        def username():
            return 'username'

        @pytest.fixture
        def other_username(username):
            return 'other-' + username

    test_something.py
        # content of tests/test_something.py
        import pytest

        @pytest.mark.parametrize('username', ['directly-overridden-username'])
        def test_username(username):
            assert username == 'directly-overridden-username'

        @pytest.mark.parametrize('username', ['directly-overridden-username-other'])
        def test_username_other(other_username):
            assert other_username == 'other-directly-overridden-username-other'

使用非参数化的fixture覆盖参数化的fixture,反之亦然

tests/
    conftest.py
        # content of tests/conftest.py
        import pytest

        @pytest.fixture(params=['one', 'two', 'three'])
        def parametrized_username(request):
            return request.param

        @pytest.fixture
        def non_parametrized_username(request):
            return 'username'

    test_something.py
        # content of tests/test_something.py
        import pytest

        @pytest.fixture
        def parametrized_username():
            return 'overridden-username'

        @pytest.fixture(params=['one', 'two', 'three'])
        def non_parametrized_username(request):
            return request.param

        def test_username(parametrized_username):
            assert parametrized_username == 'overridden-username'

        def test_parametrized_username(non_parametrized_username):
            assert non_parametrized_username in ['one', 'two', 'three']

    test_something_else.py
        # content of tests/test_something_else.py
        def test_username(parametrized_username):
            assert parametrized_username in ['one', 'two', 'three']

        def test_username(non_parametrized_username):
            assert non_parametrized_username == 'username'

使用来自其他项目的fixture

通常,提供pytest支持的项目会使用入口点( entry points),因此只需将这些项目安装到环境中,就可以使这些fixture可供使用。

如果你想要用来自不使用入口点的项目的fixture,你可以在你的顶级conftest.py文件中定义pytest_plugins来将该模块注册为插件。

假设你在mylibrary.fixtures中有一些fixture,并且你想要在你的app/tests目录中重用它们。

你所需要做的就是在app/tests/conftest.py中定义pytest_plugins并指向该模块。

pytest_plugins = "mylibrary.fixtures"

这实际上将mylibrary.fixtures注册为一个插件,使其所有fixture和钩子对app/tests中的测试可用。

注意

有时用户会从其他项目中导入fixture以供使用,但这并不推荐:将fixture导入到模块中会按照该模块中定义的方式在pytest中注册它们。

这会产生一些细微的影响,比如在pytest --help中多次出现,但不推荐这样做,因为这种行为在未来的版本中可能会改变/停止工作。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值