จะยืนยันได้อย่างไรว่ามีข้อยกเว้นเกิดขึ้นใน pytest?


293

รหัส:

# coding=utf-8
import pytest


def whatever():
    return 9/0

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

เอาท์พุท:

================================ 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 ==============================

วิธีทำ pytest การพิมพ์ย้อนกลับดังนั้นฉันจะดูว่าwhateverมีการยกข้อยกเว้นในฟังก์ชันที่ใด


ฉันได้รับการติดตามย้อนกลับทั้ง Ubuntu 14.04, Python 2.7.6
thefourtheye

@thefourtheye ทำส่วนสำคัญด้วยผลลัพธ์ ฉันลองกับ Python 2.7.4 และ Ubunthu 14.04 - ด้วยผลลัพธ์เดียวกันกับที่ฉันอธิบายในโพสต์หลัก
Gates Bates

1
@GillBates คุณสามารถทำเครื่องหมายคำตอบที่ถูกต้องได้หรือไม่?
กอนซาโล่การ์เซีย

คำตอบ:


333

pytest.raises(Exception) คือสิ่งที่คุณต้องการ

รหัส

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

เอาท์พุต

============================================================================================= 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 ======================================================================================

โปรดทราบว่าe_infoบันทึกวัตถุข้อยกเว้นเพื่อให้คุณสามารถดึงรายละเอียดออกมาได้ ตัวอย่างเช่นหากคุณต้องการตรวจสอบ call call stack หรือ exception อื่น ๆ ที่อยู่ภายใน


35
มันจะดีถ้าคุณมีตัวอย่างการสอบถามจริงe_infoๆ สำหรับนักพัฒนาที่คุ้นเคยกับภาษาอื่นบางภาษาไม่ชัดเจนว่าขอบเขตของการe_infoขยายนอกwithบล็อก
cjs

153

คุณหมายถึงอะไรเช่นนี้:

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.messageใช้งานไม่ได้สำหรับฉันต้องใช้str(excinfo.value)เพิ่มคำตอบใหม่
d_j

5
@d_j assert excinfo.value.args[0] == 'some info'เป็นวิธีโดยตรงในการเข้าถึงข้อความ
maxschlepzig

assert excinfo.match(r"^some info$")ทำงานได้ดี
Robin Nemeth

14
ตั้งแต่เวอร์ชัน3.1คุณสามารถใช้อาร์กิวเมนต์คำหลักmatchเพื่อยืนยันว่าข้อยกเว้นตรงกับข้อความหรือ regex: with raises(ValueError, match='must be 0 or None'): raise ValueError("value must be 0 or None")หรือwith raises(ValueError, match=r'must be \d+$'): raise ValueError("value must be 42")
Ilya Rusin

58

มีสองวิธีในการจัดการกับกรณีเหล่านี้ใน pytest:

  • การใช้pytest.raisesฟังก์ชั่น

  • ใช้pytest.mark.xfailมัณฑนากร

การใช้งานของpytest.raises:

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

การใช้งานของpytest.mark.xfail:

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

ผลลัพธ์ของ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 =============================

เอาท์พุทของpytest.xfailเครื่องหมาย:

============================= 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=============================

ตามเอกสารระบุว่า:

การใช้pytest.raisesมีแนวโน้มที่จะดีกว่าสำหรับกรณีที่คุณกำลังทดสอบข้อยกเว้นโค้ดของคุณเองกำลังเพิ่มขึ้นอย่างจงใจในขณะที่ใช้@pytest.mark.xfailกับฟังก์ชั่นตรวจสอบอาจจะดีกว่าสำหรับบางอย่างเช่นการทำเอกสารข้อผิดพลาดที่ไม่มีการผสม .


xfailไม่ใช่วิธีการแก้ปัญหาที่นี่มันเพียงช่วยให้การทดสอบล้มเหลว ที่นี่เราต้องการตรวจสอบว่ามีข้อยกเว้นบางอย่างเกิดขึ้นหรือไม่
Ctrl-C

44

คุณสามารถลอง

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

ในการรับข้อความแสดงข้อยกเว้น / ค่าเป็นสตริงใน pytest 5.0.0 str(excinfo.value)จำเป็นต้องใช้ มันยังทำงานใน pytest 4.x ใน pytest 4.x str(excinfo)ก็ใช้งานได้ แต่ไม่ทำงานใน pytest 5.0.0
Makyen

นี่คือสิ่งที่ฉันกำลังมองหา ขอบคุณ
Eystein Bye

16

pytest วิวัฒนาการอย่างต่อเนื่องและด้วยการเปลี่ยนแปลงที่ดีในอดีตที่ผ่านมาตอนนี้มันเป็นไปได้ที่จะทดสอบพร้อมกัน

  • ประเภทข้อยกเว้น (ทดสอบอย่างเข้มงวด)
  • ข้อความแสดงข้อผิดพลาด (ตรวจสอบอย่างเข้มงวดหรือหลวมโดยใช้นิพจน์ทั่วไป)

สองตัวอย่างจากเอกสาร:

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

ฉันใช้วิธีการนั้นในหลายโครงการและชอบมาก


6

ใช้วิธีที่ถูกต้องpytest.raisesแต่ฉันพบวิธีอื่นที่น่าสนใจในความคิดเห็นที่นี่และต้องการบันทึกไว้สำหรับผู้อ่านในอนาคตของคำถามนี้:

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


2

การฝึกฝนที่ดีกว่านี้คือการใช้คลาสที่สืบทอด unittestTestCase และรัน self.assertRaises

ตัวอย่างเช่น:

import unittest


def whatever():
    return 9/0


class TestWhatEver(unittest.TestCase):

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

จากนั้นคุณจะรันมันโดยการเรียกใช้:

pytest -vs test_path

4
ฝึกดีกว่าอะไร ฉันจะไม่เรียกใช้ซินแท็กซ์แบบ unittest แทนที่จะใช้ pytest syntax "better practice" เลย
Jean-François Corbett

2
อาจไม่ดีขึ้น แต่เป็นทางเลือกที่มีประโยชน์ เนื่องจากเกณฑ์สำหรับคำตอบคือประโยชน์ฉันจึง upvoting
Reb.Cabin

ดูเหมือนว่าpytestจะได้รับความนิยมมากกว่าnosexแต่นี่เป็นวิธีที่ฉันใช้ pytest
แก๊งค์

0

คุณพยายามลบ "pytrace = True" หรือไม่

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

คุณได้ลองใช้งานด้วย '--fulltrace' แล้วหรือยัง

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.