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.