如何使用pytest检查不会引发错误


83

假设我们有这样的东西:

import py, pytest

ERROR1 = ' --- Error : value < 5! ---'
ERROR2 = ' --- Error : value > 10! ---'

class MyError(Exception):
    def __init__(self, m):
        self.m = m

    def __str__(self):
        return self.m

def foo(i):
    if i < 5:
        raise MyError(ERROR1)
    elif i > 10:
        raise MyError(ERROR2)
    return i


# ---------------------- TESTS -------------------------
def test_foo1():
    with pytest.raises(MyError) as e:
        foo(3)
    assert ERROR1 in str(e)

def test_foo2():
    with pytest.raises(MyError) as e:
        foo(11)
    assert ERROR2 in str(e)

def test_foo3():
        ....
        foo(7)
         ....

问:如何使test_foo3()进行测试,但不会引发MyError?很明显,我可以测试一下:

def test_foo3():
    assert foo(7) == 7

但我想通过pytest.raises()进行测试。有可能吗?例如:在某种情况下,该函数“ foo”根本没有返回值,

def foo(i):
    if i < 5:
        raise MyError(ERROR1)
    elif i > 10:
        raise MyError(ERROR2)

恕我直言,以这种方式进行测试可能很有意义。


看起来在寻找问题,代码测试foo(7)很好。您将获得正确的消息,并且使用所有pytest输出进行调试将更加容易。您从@Faruk('Unexpected error...')强制执行的建议对该错误一无所获,您将陷入困境。要使它变得更好,唯一可以做的就是说出自己的意图test_foo3_works_on_integers_within_range()
dhill

Answers:


125

如果测试引发任何意外的异常,则测试将失败。您只需调用foo(7),就可以测试没有引发MyError了。因此,只需满足以下条件:

def test_foo3():
    foo(7)

如果要明确并为此编写断言语句,则可以执行以下操作:

def test_foo3():
    try:
        foo(7)
    except MyError:
        pytest.fail("Unexpected MyError ..")

3
谢谢,它可以工作,但它似乎更多的是hack,而不是一个干净的解决方案。例如,对foo(4)的测试将失败,但不是由于断言错误。
paraklet

foo(4)的测试将失败,因为它将引发意外的异常。另一种方法是将其包装在try catch块中,并通过特定消息失败。我将更新我的答案。
法鲁克·沙欣

1
如果您有很多这样的情况,那么用一个简单的函数编写它可能会有用:```def not_raises(error_class,func,* args,** kwargs):...```或者您可以编写一个像pytest这样的方法。如果您愿意,我建议您为此写一份公关使所有人受益。:)(存储库位于bitbucket中)。
布鲁诺·奥利维拉

6
@paraklet-pytest的主要口号是“无样板测试”。pytest的精神非常重要,它使您能够像Faruk的第一个示例一样编写测试,而pytest为您处理细节。对我来说,第一个例子是“干净的解决方案”,第二个例子似乎不必要地冗长。
Nick Chammas 2015年

21

建立在Oisin提到的之上。

您可以创建一个简单的not_raises函数,使其类似于pytest的行为raises

from contextlib import contextmanager

@contextmanager
def not_raises(exception):
  try:
    yield
  except exception:
    raise pytest.fail("DID RAISE {0}".format(exception))

如果您想坚持raises拥有对方,因此您的测试更具可读性,那就很好。但是,从本质上讲,您只需要单独运行要测试的代码块就不需要任何其他操作-pytest会在该块引发错误后立即失败。


1
我希望这内置于py.test; 在某些情况下,它会使测试更具可读性。特别是与@pytest.mark.parametrize
Arel

我非常感谢这种方法对代码的可读性!
GrazingScientist

6

我很好奇,看看not_raises是否行得通。一个快速测试是(test_notraises.py):

from contextlib import contextmanager

@contextmanager
def not_raises(ExpectedException):
    try:
        yield

    except ExpectedException, err:
        raise AssertionError(
            "Did raise exception {0} when it should not!".format(
                repr(ExpectedException)
            )
        )

    except Exception, err:
        raise AssertionError(
            "An unexpected exception {0} raised.".format(repr(err))
        )

def good_func():
    print "hello"


def bad_func():
    raise ValueError("BOOM!")


def ugly_func():
    raise IndexError("UNEXPECTED BOOM!")


def test_ok():
    with not_raises(ValueError):
        good_func()


def test_bad():
    with not_raises(ValueError):
        bad_func()


def test_ugly():
    with not_raises(ValueError):
        ugly_func()

它似乎确实起作用。但是我不确定它在测试中是否真的读得好。


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.