如何测试pytest fixture是否会引发异常?

时间:2018-05-14 14:02:43

标签: 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

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

============================== 1 xfailed in 0.15 seconds ========================

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

有更好的方法吗?我可以以某种方式模拟pytest命令行选项,我不需要通过pytest --<commandline-option-a> <test-file-name>::<test-name>调用特定测试。

1 个答案:

答案 0 :(得分:1)

初始设置

假设您有一个包含以下代码的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))

它添加了一个新的命令行arg --foo和一个fixture foo返回传递的arg,如果没有指定则返回bar。除bar之外的任何其他内容通过--foo传递时,该夹具会引发ValueError

您可以照常使用灯具,例如

def test_something(foo):
    assert foo == 'bar'

现在让我们测试那个灯具。

制剂

在这个例子中,我们需要先做一些简单的重构。将夹具和相关代码移动到一个名为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))

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

# conftest.py

pytest_plugins = ['my_plugin']

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

激活pytester

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

# conftest.py

pytest_plugins = ['my_plugin', 'pytester']

编写测试

一旦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)

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

让我们添加另一项测试,检查foo是否会引发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'
    ])

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