Cách sử dụng pytest để kiểm tra xem Lỗi KHÔNG được phát sinh


83

Giả sử chúng ta có smth như thế:

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)
         ....

H: Làm cách nào tôi có thể tạo test_foo3 () để kiểm tra mà không có MyError nào xuất hiện? Rõ ràng là tôi có thể kiểm tra:

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

nhưng tôi muốn kiểm tra điều đó thông qua pytest.raises (). Có thể bằng cách nào đó? Ví dụ: trong trường hợp, hàm "foo" không có giá trị trả về nào cả,

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

nó có thể hợp lý để kiểm tra theo cách này, imho.


Có vẻ như đang tìm kiếm sự cố, kiểm tra mã foo(7)vẫn ổn. Bạn sẽ nhận được thông báo phù hợp và sẽ dễ dàng gỡ lỗi hơn với tất cả đầu ra pytest. Đề xuất bạn buộc từ @Faruk ( 'Unexpected error...') không nói gì về lỗi và bạn sẽ gặp khó khăn. Điều duy nhất bạn có thể làm để làm cho nó tốt hơn là nêu rõ ý định của bạn như thế nào test_foo3_works_on_integers_within_range().
dhill

Câu trả lời:


125

Một bài kiểm tra sẽ thất bại nếu nó phát sinh bất kỳ loại Ngoại lệ không mong muốn nào. Bạn chỉ cần gọi foo (7) và bạn sẽ kiểm tra được rằng không có MyError nào được đưa ra. Vì vậy, sau đây là đủ:

def test_foo3():
    foo(7)

Nếu bạn muốn rõ ràng và viết một tuyên bố khẳng định cho điều này, bạn có thể làm:

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

3
Cảm ơn, nó hoạt động, nhưng nó có vẻ là một hack hơn là một giải pháp sạch. Ví dụ: kiểm tra foo (4) sẽ không thành công, nhưng không phải do lỗi xác nhận.
paraklet

kiểm tra foo (4) sẽ không thành công vì nó sẽ ném ra một ngoại lệ không được mong đợi. Một cách khác là gói nó trong một khối try catch và không thành công với một thông báo cụ thể. Tôi sẽ cập nhật câu trả lời của tôi.
Faruk Sahin

1
Nếu bạn gặp nhiều trường hợp như thế này, có thể hữu ích khi viết nó trong một hàm đơn giản: `` `def not_raises (error_class, func, * args, ** kwargs): ... '' 'Hoặc bạn có thể viết một với cách tiếp cận tương tự như pytest. Nếu bạn làm vậy, tôi khuyên bạn nên viết một bài PR với điều này để mang lại lợi ích cho tất cả. :) (Kho lưu trữ bằng bitbucket ).
Bruno Oliveira

6
@paraklet - khẩu hiệu chính của pytest là "thử nghiệm không có bảng soạn sẵn" . Đó là tinh thần của pytest để cho phép bạn viết các bài kiểm tra như trong ví dụ đầu tiên của Faruk trong khi pytest xử lý các chi tiết cho bạn. Đối với tôi, ví dụ đầu tiên là "giải pháp sạch" và thứ hai có vẻ dài dòng một cách không cần thiết.
Nick Chammas

21

Xây dựng dựa trên những gì Oisin đã đề cập ..

Bạn có thể tạo một not_raiseshàm đơn giản hoạt động tương tự như pytest raises:

from contextlib import contextmanager

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

Điều này là tốt nếu bạn muốn raisescó một đối tác và do đó các bài kiểm tra của bạn dễ đọc hơn. Tuy nhiên, về bản chất, bạn không thực sự cần gì ngoài việc chỉ chạy khối mã bạn muốn kiểm tra trên dòng của chính nó - dù sao thì pytest cũng sẽ thất bại ngay khi khối đó phát sinh lỗi.


1
Tôi ước điều này được tích hợp vào py.test; nó sẽ làm cho các bài kiểm tra dễ đọc hơn trong một số trường hợp. Đặc biệt là kết hợp với @pytest.mark.parametrize.
Arel

Tôi đánh giá cao cảm giác dễ đọc mã trong cách tiếp cận này!
GrazingScientist

6

Tôi tò mò muốn biết liệu not_raises có hoạt động không. Một thử nghiệm nhanh về điều này là (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()

Nó dường như hoạt động. Tuy nhiên tôi không chắc liệu nó có thực sự đọc tốt trong bài kiểm tra hay không.


Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.