Làm thế nào để khẳng định đúng rằng một ngoại lệ được nêu ra trong pytest?


292

Mã số:

# coding=utf-8
import pytest


def whatever():
    return 9/0

def test_whatever():
    try:
        whatever()
    except ZeroDivisionError as exc:
        pytest.fail(exc, pytrace=True)

Đầu ra:

================================ test session starts =================================
platform linux2 -- Python 2.7.3 -- py-1.4.20 -- pytest-2.5.2
plugins: django, cov
collected 1 items 

pytest_test.py F

====================================== FAILURES ======================================
___________________________________ test_whatever ____________________________________

    def test_whatever():
        try:
            whatever()
        except ZeroDivisionError as exc:
>           pytest.fail(exc, pytrace=True)
E           Failed: integer division or modulo by zero

pytest_test.py:12: Failed
============================== 1 failed in 1.16 seconds ==============================

Làm thế nào để tạo ra dấu vết in ấn pytest, vì vậy tôi sẽ thấy nơi nào trong whateverchức năng một ngoại lệ được nêu ra?


Tôi nhận được toàn bộ truy nguyên, Ubuntu 14.04, Python 2.7.6
thefourtheye

@thefourtheye Làm cho ý chính với đầu ra xin vui lòng. Tôi đã thử với Python 2.7.4 và Ubunthu 14.04 - với kết quả tương tự như tôi đã mô tả trong bài viết chính.
Gill Bates

1
@GillBates bạn có thể đánh dấu câu trả lời đúng không ??
Gonzalo Garcia

Câu trả lời:


330

pytest.raises(Exception) là những gì bạn cần.

import pytest

def test_passes():
    with pytest.raises(Exception) as e_info:
        x = 1 / 0

def test_passes_without_info():
    with pytest.raises(Exception):
        x = 1 / 0

def test_fails():
    with pytest.raises(Exception) as e_info:
        x = 1 / 1

def test_fails_without_info():
    with pytest.raises(Exception):
        x = 1 / 1

# Don't do this. Assertions are caught as exceptions.
def test_passes_but_should_not():
    try:
        x = 1 / 1
        assert False
    except Exception:
        assert True

# Even if the appropriate exception is caught, it is bad style,
# because the test result is less informative
# than it would be with pytest.raises(e)
# (it just says pass or fail.)

def test_passes_but_bad_style():
    try:
        x = 1 / 0
        assert False
    except ZeroDivisionError:
        assert True

def test_fails_but_bad_style():
    try:
        x = 1 / 1
        assert False
    except ZeroDivisionError:
        assert True

Đầu ra

============================================================================================= test session starts ==============================================================================================
platform linux2 -- Python 2.7.6 -- py-1.4.26 -- pytest-2.6.4
collected 7 items 

test.py ..FF..F

=================================================================================================== FAILURES ===================================================================================================
__________________________________________________________________________________________________ test_fails __________________________________________________________________________________________________

    def test_fails():
        with pytest.raises(Exception) as e_info:
>           x = 1 / 1
E           Failed: DID NOT RAISE

test.py:13: Failed
___________________________________________________________________________________________ test_fails_without_info ____________________________________________________________________________________________

    def test_fails_without_info():
        with pytest.raises(Exception):
>           x = 1 / 1
E           Failed: DID NOT RAISE

test.py:17: Failed
___________________________________________________________________________________________ test_fails_but_bad_style ___________________________________________________________________________________________

    def test_fails_but_bad_style():
        try:
            x = 1 / 1
>           assert False
E           assert False

test.py:43: AssertionError
====================================================================================== 3 failed, 4 passed in 0.02 seconds ======================================================================================

Lưu ý rằng e_infolưu đối tượng ngoại lệ để bạn có thể trích xuất chi tiết từ nó. Ví dụ: nếu bạn muốn kiểm tra ngăn xếp cuộc gọi ngoại lệ hoặc ngoại lệ lồng nhau khác bên trong.


34
Sẽ tốt hơn nếu bạn có thể bao gồm một ví dụ thực sự truy vấn e_info. Đối với các nhà phát triển quen thuộc hơn với một số ngôn ngữ khác, không rõ ràng rằng phạm vi e_infomở rộng ra bên ngoài withkhối.
cjs

152

Bạn có ý nghĩa như thế này:

def test_raises():
    with pytest.raises(Exception) as execinfo:   
        raise Exception('some info')
    # these asserts are identical; you can use either one   
    assert execinfo.value.args[0] == 'some info'
    assert str(execinfo.value) == 'some info'

16
excinfo.value.messagekhông làm việc cho tôi, phải sử dụng str(excinfo.value), đã thêm một câu trả lời mới
d_j

5
@d_j, assert excinfo.value.args[0] == 'some info'là cách trực tiếp để truy cập tin nhắn
maxschlepzig

assert excinfo.match(r"^some info$")hoạt động tốt
Robin Nemeth

14
Vì phiên bản, 3.1bạn có thể sử dụng đối số từ khóa matchđể xác nhận rằng ngoại lệ khớp với văn bản hoặc biểu thức chính quy: with raises(ValueError, match='must be 0 or None'): raise ValueError("value must be 0 or None")hoặcwith raises(ValueError, match=r'must be \d+$'): raise ValueError("value must be 42")
Ilya Rusin

58

Có hai cách để xử lý các loại trường hợp này trong pytest:

  • Sử dụng pytest.raiseschức năng

  • Sử dụng pytest.mark.xfailtrang trí

Cách sử dụng pytest.raises:

def whatever():
    return 9/0
def test_whatever():
    with pytest.raises(ZeroDivisionError):
        whatever()

Cách sử dụng pytest.mark.xfail:

@pytest.mark.xfail(raises=ZeroDivisionError)
def test_whatever():
    whatever()

Đầu ra của pytest.raises:

============================= test session starts ============================
platform linux2 -- Python 2.7.10, pytest-3.2.3, py-1.4.34, pluggy-0.4.0 -- 
/usr/local/python_2.7_10/bin/python
cachedir: .cache
rootdir: /home/user, inifile:
collected 1 item

test_fun.py::test_whatever PASSED


======================== 1 passed in 0.01 seconds =============================

Đầu ra của pytest.xfailđiểm đánh dấu:

============================= test session starts ============================
platform linux2 -- Python 2.7.10, pytest-3.2.3, py-1.4.34, pluggy-0.4.0 -- 
/usr/local/python_2.7_10/bin/python
cachedir: .cache
rootdir: /home/user, inifile:
collected 1 item

test_fun.py::test_whatever xfail

======================== 1 xfailed in 0.03 seconds=============================

Như tài liệu nói:

Việc sử dụng pytest.raisescó thể tốt hơn cho các trường hợp bạn đang kiểm tra ngoại lệ, mã của bạn đang cố tình nâng cao, trong khi sử dụng @pytest.mark.xfailvới chức năng kiểm tra có thể tốt hơn cho những thứ như ghi lại các lỗi không được trộn lẫn (trong đó thử nghiệm mô tả những gì xảy ra khi xảy ra lỗi). .


xfailkhông phải là giải pháp cho vấn đề ở đây, nó chỉ cho phép thử nghiệm thất bại. Ở đây chúng tôi muốn kiểm tra nếu một ngoại lệ nhất định được nêu ra.
Ctrl-C

44

bạn co thể thử

def test_exception():
    with pytest.raises(Exception) as excinfo:   
        function_that_raises_exception()   
    assert str(excinfo.value) == 'some info' 

Để có được thông báo / giá trị ngoại lệ dưới dạng chuỗi trong pytest 5.0.0, sử dụng str(excinfo.value)là bắt buộc. Nó cũng hoạt động trong pytest 4.x. Trong pytest 4.x, str(excinfo)cũng hoạt động, nhưng không hoạt động trong pytest 5.0.0.
Makyen

Đây là những gì tôi đang tìm kiếm. Cảm ơn bạn
Eystein Bye

15

pytest liên tục phát triển và với một trong những thay đổi tốt đẹp trong quá khứ gần đây, giờ đây có thể đồng thời kiểm tra

  • loại ngoại lệ (kiểm tra nghiêm ngặt)
  • thông báo lỗi (kiểm tra nghiêm ngặt hoặc lỏng lẻo bằng cách sử dụng biểu thức chính quy)

Hai ví dụ từ tài liệu:

with pytest.raises(ValueError, match='must be 0 or None'):
    raise ValueError('value must be 0 or None')
with pytest.raises(ValueError, match=r'must be \d+$'):
    raise ValueError('value must be 42')

Tôi đã sử dụng phương pháp đó trong một số dự án và rất thích nó.


6

Đúng cách đang sử dụng pytest.raisesnhưng tôi đã tìm thấy cách thay thế thú vị trong các bình luận ở đây và muốn lưu nó cho những độc giả tương lai của câu hỏi này:

try:
    thing_that_rasises_typeerror()
    assert False
except TypeError:
    assert True

4

Giải pháp này là những gì chúng tôi đang sử dụng:

def test_date_invalidformat():
    """
    Test if input incorrect data will raises ValueError exception
    """
    date = "06/21/2018 00:00:00"
    with pytest.raises(ValueError):
        app.func(date) #my function to be tested

Vui lòng tham khảo pytest, https://docs.pytest.org/en/latest/reference.html#pytest-raises


2

Thực hành tốt hơn sẽ sử dụng một lớp kế thừa unittest.TestCase và chạy self.assertRaises.

Ví dụ:

import unittest


def whatever():
    return 9/0


class TestWhatEver(unittest.TestCase):

    def test_whatever():
        with self.assertRaises(ZeroDivisionError):
            whatever()

Sau đó, bạn sẽ thực hiện nó bằng cách chạy:

pytest -vs test_path

4
Thực hành tốt hơn những gì? Tôi sẽ không gọi bằng cách sử dụng cú pháp nhỏ nhất thay vì cú pháp pytest "thực hành tốt hơn" cả.
Jean-François Corbett

2
Nó có thể không 'tốt hơn', nhưng đó là một sự thay thế hữu ích. Bởi vì tiêu chí cho một câu trả lời là tiện ích, tôi đang nâng cao.
Reb.Cabin

Có vẻ như pytestlà phổ biến hơn nosex, nhưng đây là cách tôi sử dụng pytest.
Gang

0

Bạn đã thử xóa "pytrace = True" chưa?

pytest.fail(exc, pytrace=True) # before
pytest.fail(exc) # after

Bạn đã thử chạy với '--fulltrace' chưa?

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.