Pytest'te bir istisnanın ortaya çıktığını doğru bir şekilde nasıl iddia edebilirim?


293

Kod:

# coding=utf-8
import pytest


def whatever():
    return 9/0

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

Çıktı:

================================ 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 print traceback nasıl yapılır, bu yüzden whateverfonksiyonda nerede bir istisna ortaya çıktı görmek istiyorum?


Tüm izlemeyi alıyorum, Ubuntu 14.04, Python 2.7.6
thefourtheye

@thefourtheye Çıktı ile gist yapın lütfen. Python 2.7.4 ve Ubunthu 14.04 ile denedim - ana yazıda tarif ettiğim sonuçla.
Gill Bates

1
@GillBates u doğru cevabı işaretleyebilir ??
Gonzalo Garcia

Yanıtlar:


333

pytest.raises(Exception) ihtiyacınız olan şey bu.

kod

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

Çıktı

============================================================================================= 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_infoAyrıntıları ayıklayabilmeniz için istisna nesnesini kaydettiğini unutmayın . Örneğin, kural dışı durum çağrısı yığınını veya içindeki başka bir iç içe kural dışı durumu kontrol etmek istiyorsanız.


35
Aslında sorgulama yapan bir örnek eklemek iyi olur e_info. Bazı diğer dillere daha aşina olan geliştiriciler için, kapsamın bloğun e_infodışında uzandığı açık değildir with.
cjs

153

Bunun gibi birşey mi demek istedin:

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.messagebenim için çalışmadı, kullanmak zorunda kaldı str(excinfo.value), yeni bir cevap ekledi
d_j

5
@d_j, assert excinfo.value.args[0] == 'some info'mesaja erişmenin doğrudan yoludur
maxschlepzig

assert excinfo.match(r"^some info$")iyi çalışıyor
Robin Nemeth

14
Sürümünde beri 3.1anahtar kelime argümanını kullanabilirsiniz match: istisnası metin veya normal ifadeyle eşleşir iddia etmek with raises(ValueError, match='must be 0 or None'): raise ValueError("value must be 0 or None")veyawith raises(ValueError, match=r'must be \d+$'): raise ValueError("value must be 42")
Ilya Rusin

58

Bu tür davaları pytest'te ele almanın iki yolu vardır:

  • pytest.raisesFonksiyonu kullanarak

  • pytest.mark.xfailDekoratör kullanma

Kullanımı pytest.raises:

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

Kullanımı pytest.mark.xfail:

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

Çıktı 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 =============================

Çıktı pytest.xfailişaretleyici:

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

Gibi belgeler diyor ki:

Kullanılması pytest.raiseskullanarak oysa, muhtemelen kendi kod kasten yükselttiğini istisnalar test ediyoruz durumları daha iyi olması için olan @pytest.mark.xfailbir çek fonksiyonu ile bağımlılıkları (test gerçekleşmesi “gerektiğini” söyler) sabitlenmemiş hataları belgelemek gibi bir şey ya da hatalar için muhtemelen daha iyidir .


xfailburada sorunun çözümü değildir, sadece testin başarısız olmasına izin verir. Burada belirli bir istisnanın ortaya çıkıp çıkmadığını kontrol etmek istiyoruz.
Ctrl-C

44

deneyebilirsin

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

İstisna iletisini / değerini, 5.0.0 pytestinde bir dize olarak almak için kullanmak str(excinfo.value)gerekir. Ayrıca pyx 4.x de çalışır. Pytest 4.x, str(excinfo)ayrıca çalışır, ancak yok değil pytest 5.0.0 çalışır.
Makyen

Aradığım şey buydu. Eyvallah
Eystein Bye

16

pytest sürekli gelişir ve yakın geçmişteki hoş değişikliklerden biriyle şimdi aynı anda test etmek mümkündür

  • istisna türü (katı test)
  • hata mesajı (normal bir ifade kullanarak katı veya gevşek kontrol)

Belgelerden iki örnek:

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

Bu yaklaşımı bir dizi projede kullanıyorum ve çok beğendim.


6

Doğru yol kullanıyor pytest.raisesama burada yorumlarda ilginç bir alternatif yol buldum ve bu sorunun gelecekteki okuyucuları için kaydetmek istiyorum:

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


2

Daha iyi uygulama, unittest.TestCase öğesini devralan ve self.assertRaises çalıştıran bir sınıf kullanmak olacaktır.

Örneğin:

import unittest


def whatever():
    return 9/0


class TestWhatEver(unittest.TestCase):

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

Sonra çalıştırarak yürütürsünüz:

pytest -vs test_path

4
Hangisinden daha iyi pratik? Ben hiç "daha iyi uygulama" pytest sözdizimi yerine unittest sözdizimi kullanarak çağırmak olmaz.
Jean-François Corbett

2
'Daha iyi' olmayabilir ama yararlı bir alternatif. Bir cevap kriteri faydalı olduğu için ben yükseltiyorum.
Reb.Cabin

pytestdaha popüler gibi görünüyor nosex, ama ben bu yüzden pytest kullanıyorum.
Gang

0

"Pytrace = True" kodunu kaldırmayı denediniz mi?

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

'--Fulltrace' ile koşmaya çalıştın mı?

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.