2013-11-28 16 views
19

Supponiamo che abbiamo dovrebbe occupare del genere:Come utilizzare pytest per controllare che l'errore non viene generato

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

D: Come posso fare test_foo3() per testare, che nessuna MyError è sollevata? E 'ovvio, che ho potuto solo prova:

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

ma voglio provare che via pytest.raises(). È possibile in qualche modo? Ad esempio: in un caso, tale funzione "foo" non ha alcun ritorno valore,

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

potrebbe avere senso per testare questo modo, imho.

+0

Sembra cercare un problema, il test del codice 'foo (7)' va bene. Otterrai il messaggio giusto e sarà più facile eseguire il debug con tutti gli output pytest. Il suggerimento che hai forzato da @Faruk (''Errore inaspettato ...'') non dice nulla sull'errore e rimarrai bloccato. L'unica cosa che puoi fare per renderlo migliore è dichiarare la tua intenzione come 'test_foo3_works_on_integers_within_range()'. – dhill

risposta

36

Un test avrà esito negativo se solleva qualsiasi tipo di Eccezione imprevista. Puoi semplicemente invocare foo (7) e avrai verificato che nessun MyError viene generato. Così, dopo sarà sufficiente:

def test_foo3(): 
    foo(7) 

Se davvero si vuole scrivere un assert per questo, si può provare:

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

Grazie, funziona, ma sembra essere più un trucco, che una soluzione pulita. Ad esempio, test for foo (4) fallirà, ma non a causa di errore di asserzione. Il test – paraklet

+0

per foo (4) fallirà perché genererà un'eccezione che non era prevista. Un altro modo sarebbe quello di avvolgerlo in un blocco catch try e fallire con un messaggio specifico. Aggiornerò la mia risposta –

+0

Se si dispone di un sacco di casi come questo potrebbe essere utile scrivere che in un semplice funzione: '' ' def not_raises (error_class, func, * args, ** kwargs): ... ' ' ' Oppure puoi scrivere un approccio simile a quello di pytest. Se lo fai ti suggerisco di scrivere un PR con questo per beneficiare tutti. :) (Il repository è in [bitbucket] (https://bitbucket.org/hpk42/pytest)). –

3

ero curioso di vedere se un not_raises avrebbe funzionato. Un rapido test di questo è (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() 

Non sembra funzionare. Tuttavia non sono sicuro che sia davvero valido nel test .

3

Basandosi sulla cima di quello che Oisin menzionato ..

È possibile effettuare una semplice funzione not_raises che agisce simile a quella di pytest raises:

from contextlib import contextmanager 

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

Questo va bene se si vuole attaccare ad avere raises avere una controparte e quindi i tuoi test sono più leggibili. In sostanza, tuttavia, non è necessario nulla oltre al semplice esecuzione del blocco di codice che si desidera testare sulla propria linea - pytest fallirà comunque non appena quel blocco genera un errore.

+0

Vorrei che questo fosse stato creato in py.test; in alcuni casi renderebbe le prove molto più leggibili. Specialmente in congiunzione con '@ pytest.mark.parametrize'. – Arel