如何测试 pytest 夹具是否引发异常?

thi*_*ybk 4 python testing pytest

用例:在pytest测试套件中@fixture,如果缺少其配置的命令行选项,我将引发异常。我已经使用以下方法为此夹具编写了一个测试xfail

import pytest
from <module> import <exception>

@pytest.mark.xfail(raises=<exception>)
def test_fixture_with_missing_options_raises_exception(rc_visard):
    pass
Run Code Online (Sandbox Code Playgroud)

但是,运行测试后的输出并未将测试声明为通过,而是“xfailed”:

============================== 1 xfailed in 0.15 seconds ========================
Run Code Online (Sandbox Code Playgroud)

除此之外,我无法测试是否fixture会引发特定缺失命令行选项的异常。

有没有更好的方法来做到这一点?我可以pytest以某种方式模拟命令行选项,我不需要通过pytest --<commandline-option-a> <test-file-name>::<test-name>.

hoe*_*ing 6

最初设定

假设您有一个conftest.py包含以下代码的简化项目:

import pytest


def pytest_addoption(parser):
    parser.addoption('--foo', action='store', dest='foo', default='bar',
                     help='--foo should be always bar!')

@pytest.fixture
def foo(request):
    fooval = request.config.getoption('foo')
    if fooval != 'bar':
        raise ValueError('expected foo to be "bar"; "{}" provided'.format(fooval))
Run Code Online (Sandbox Code Playgroud)

它添加了一个新的命令行 arg--foo和一个foo返回传递的 arg的固定装置,或者bar如果未指定。如果除了bar通过 via之外还有其他任何事情--foo,夹具会引发ValueError.

例如,您像往常一样使用夹具

def test_something(foo):
    assert foo == 'bar'
Run Code Online (Sandbox Code Playgroud)

现在让我们测试该夹具。

制剂

在这个例子中,我们需要先做一些简单的重构。将夹具和相关代码移动到某个名为其他内容的文件中conftest.py,例如,my_plugin.py

# my_plugin.py

import pytest


def pytest_addoption(parser):
    parser.addoption('--foo', action='store', dest='foo', default='bar',
                     help='--foo should be always bar!')

@pytest.fixture
def foo(request):
    fooval = request.config.getoption('foo')
    if fooval != 'bar':
        raise ValueError('expected foo to be "bar"; "{}" provided'.format(fooval))
Run Code Online (Sandbox Code Playgroud)

在 中conftest.py,确保加载了新插件:

# conftest.py

pytest_plugins = ['my_plugin']
Run Code Online (Sandbox Code Playgroud)

运行现有的测试套件以确保我们没有破坏任何东西,所有测试仍然应该通过。

启用 pytester

pytest提供了一个额外的插件来编写插件测试,称为pytester. 默认情况下它不会激活,因此您应该手动执行此操作。在 中conftest.py,使用以下命令扩展插件列表pytester

# conftest.py

pytest_plugins = ['my_plugin', 'pytester']
Run Code Online (Sandbox Code Playgroud)

编写测试

一旦pytester处于活动状态,您将获得一个名为testdir. 它可以pytest从代码生成和运行测试套件。这是我们的第一个测试的样子:

# test_foo_fixture.py

def test_all_ok(testdir):

    testdata = '''
               def test_sample(foo):
                   assert True
               '''

    testconftest = '''
                   pytest_plugins = ['my_plugin']
                   '''

    testdir.makeconftest(testconftest)
    testdir.makepyfile(testdata)
    result = testdir.runpytest()
    result.assert_outcomes(passed=1)
Run Code Online (Sandbox Code Playgroud)

这里发生的事情应该很明显:我们将测试代码作为字符串提供,testdir并将pytest在某个临时目录中从中生成一个项目。为了确保我们的foo夹具在生成的测试项目中可用,我们以与conftest在真实项目中相同的生成方式传递它。testdir.runpytest()开始测试运行,产生我们可以检查的结果。

让我们添加另一个测试来检查是否foo会引发 a ValueError

def test_foo_valueerror_raised(testdir):
    testdata = '''
               def test_sample(foo):
                   assert True
               '''

    testconftest = '''
                   pytest_plugins = ['my_plugin']
                   '''

    testdir.makeconftest(testconftest)
    testdir.makepyfile(testdata)
    result = testdir.runpytest('--foo', 'baz')                                                                                                                                
    result.assert_outcomes(error=1)
    result.stdout.fnmatch_lines([
        '*ValueError: expected foo to be "bar"; "baz" provided'
    ])
Run Code Online (Sandbox Code Playgroud)

在这里,我们执行生成的测试,--foo baz然后验证是否一个测试以错误结束并且错误输出包含预期的错误消息。