2013-11-28 19 views
19

假設我們有水木清華這樣的:如何使用pytest檢查誤差不提出

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

問:我怎樣才能讓test_foo3()來測試,沒有MyError提高? 很明顯,我可以只測試:

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

,但我想測試通過pytest.raises()。有可能嗎? 例如:在情況下,「foo」函數沒有返回值可言,

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

它可能是有意義的測試這種方式,恕我直言。

+0

它看起來像尋找一個問題,代碼測試'foo(7)'是好的。您將得到正確的信息,並且使用pytest的所有輸出將更容易進行調試。你從@Faruk(''Unexpected error ...''')強制提出的建議沒有提到有關錯誤的信息,你會堅持下去。你可以做的唯一事情就是說明你的意圖,比如'test_foo3_works_on_integers_within_range()'。 – dhill

回答

36

如果引發任何意外的異常,測試將失敗。你可以調用foo(7),你會測試沒有引發MyError。因此,以下就足夠了:

def test_foo3(): 
    foo(7) 

如果你真的想要寫這個斷言語句,你可以嘗試:

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

謝謝,它的工作原理,但它似乎是一個黑客,而不是一個乾淨的解決方案。例如,對foo(4)的測試會失敗,但不會由於斷言錯誤。 – paraklet

+0

foo(4)的測試將會失敗,因爲它會拋出一個並非預期的異常。另一種方法是將其封裝在try catch塊中,並以特定消息失敗。我會更新我的答案。 –

+0

如果你有很多像這樣的情況下,它可能會編寫一個簡單的功能是有用的: ''' 高清not_raises(error_class,FUNC,* ARGS,** kwargs): ... '' ' 或者你可以像pytest那樣寫一個類似於方法的方法。如果你這樣做,我建議你寫一個PR來讓所有人受益。 :) (該存儲庫位於[bitbucket](https://bitbucket.org/hpk42/pytest))。 –

3

我很好奇,看看是否not_raises會工作。這方面的一個快速測試是 (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() 

它似乎工作。但我不確定它是否真的在 測試中表現良好。

3

大廈的什麼[287]莪提到頂..

你可以做一個簡單的not_raises功能作用類似於pytest的raises

from contextlib import contextmanager 

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

如果你想堅持到具有含raises這是好的一個對手,因此你的測試更具可讀性。但實質上,除了僅僅運行你想要在自己的行上測試的代碼塊之外,你並不需要任何東西 - 只要該塊發生錯誤,pytest就會失敗。

+0

我希望這被構建到py.test中;它會使測試在某些情況下更具可讀性。特別是與'@ pytest.mark.parametrize'結合使用。 – Arel