如何测试 pytest 夹具是否引发异常? [英] How can I test if a pytest fixture raises an exception?

查看:69
本文介绍了如何测试 pytest 夹具是否引发异常?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

用例:在 pytest 测试套件中,我有一个 @fixture 如果缺少其配置的命令行选项,它会引发异常.我已经使用 xfail 为这个装置编写了一个测试:

Use case: In a pytest test suite I have a @fixture which raises exceptions if command line options for its configuration are missing. I've written a test for this fixture using xfail:

import pytest
from <module> import <exception>

@pytest.mark.xfail(raises=<exception>)
def test_fixture_with_missing_options_raises_exception(rc_visard):
    pass

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

However the output after running the tests does not state the test as passed but "xfailed" instead:

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

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

In addition to that I am not able to test if the fixture raises the exception for specific missing command line options.

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

Is there a better approach to do this? Can I mock the pytest command line options somehow that I do not need to call specific tests via pytest --<commandline-option-a> <test-file-name>::<test-name>.

推荐答案

初始设置

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

它添加了一个新的命令行参数 --foo 和一个固定装置 foo 返回传递的参数,或者 bar 如果没有指定.如果除了通过 --foo 传递的 bar 之外还有其他任何东西,fixture 会引发 ValueError.

It adds a new command line arg --foo and a fixture foo returning the passed arg, or bar if not specified. If anything else besides bar passed via --foo, the fixture raises a ValueError.

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

You use the fixture as usual, for example

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

现在让我们测试该夹具.

Now let's test that fixture.

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

In this example, we need to do some simple refactoring first. Move the fixture and related code to some file called something else than conftest.py, for example, 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 中,确保加载了新插件:

In conftest.py, ensure the new plugin is loaded:

# conftest.py

pytest_plugins = ['my_plugin']

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

Run the existing test suite to ensure we didn't break anything, all tests should still pass.

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

pytest provides an extra plugin for writing plugin tests, called pytester. It is not activated by default, so you should do that manually. In conftest.py, extend the plugins list with pytester:

# conftest.py

pytest_plugins = ['my_plugin', 'pytester']

编写测试

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

writing the tests

Once pytester is active, you get a new fixture available called testdir. It can generate and run pytest test suites from code. Here's what our first test will look like:

# 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() 开始测试运行,产生一个我们可以检查的结果.

It should be pretty obvious what happens here: we provide the tests code as string and testdir will generate a pytest project from it in some temporary directory. To ensure our foo fixture is available in the generated test project, we pass it in the generated conftest same way as we do in the real one. testdir.runpytest() starts the test run, producing a result that we can inspect.

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

Let's add another test that checks whether foo will raise 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'
    ])

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

Here we execute the generated tests with --foo baz and verify afterwards if one test ended with an error and the error output contains the expected error message.

这篇关于如何测试 pytest 夹具是否引发异常?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆