Bir Python fonksiyonunun bir istisna verdiğini nasıl test edersiniz?


Yanıtlar:


679

Unittest modülünden TestCase.assertRaises(veya TestCase.failUnlessRaises) kullanın , örneğin:

import mymod

class MyTestCase(unittest.TestCase):
    def test1(self):
        self.assertRaises(SomeCoolException, mymod.myfunc)

9
bunun tersini yapmanın bir yolu var mı? Sadece işlev istisna atarsa ​​başarısız gibi?
BUInvent

67
Argüman geçmek olduğunu Not myfuncEğer assertRaises çağrı argümanlar olarak eklemeniz gerekir. Daryl Spitzer'in cevabına bakınız.
cbron

1
çoklu istisna türlerine izin vermenin bir yolu var mı?
Dinesh

1
İddiayı hızlı bir şekilde test etmek için Python'un Yerleşik İstisnalarını kullanabilirsiniz; örneğin yukarıda Moe cevap @ kullanarak: self.assertRaises(TypeError, mymod.myfunc). Yerleşik İstisnaların tam bir listesini burada bulabilirsiniz: docs.python.org/3/library/exceptions.html#bltin-exceptions
Raymond Wachaga

3
Sınıf yapıcılarını test etmek için aynı şey:self.assertRaises(SomeCoolException, Constructor, arg1)
tschumann

476

Python 2.7'den bu yana, atılan gerçek İstisna nesnesini elde etmek için bağlam yöneticisini kullanabilirsiniz:

import unittest

def broken_function():
    raise Exception('This is broken')

class MyTestCase(unittest.TestCase):
    def test(self):
        with self.assertRaises(Exception) as context:
            broken_function()

        self.assertTrue('This is broken' in context.exception)

if __name__ == '__main__':
    unittest.main()

http://docs.python.org/dev/library/unittest.html#unittest.TestCase.assertRaises


In Python 3.5 , sen sarmak zorunda context.exceptioniçinde straksi takdirde bir alırsınız,TypeError

self.assertTrue('This is broken' in str(context.exception))

6
Python 2.7.10 kullanıyorum ve yukarıdakiler çalışmıyor; context.exceptionmesajı vermez; bir tür.
LateCoder

6
Ayrıca Python 2.7 (en azından benim 2.7.6) kullanarak import unittest2, kullanmanız gerekir str(), yani self.assertTrue('This is broken' in str(context.exception)).
Sohail Si

4
İki şey: 1. assertTrue yerine assertIn kullanabilirsiniz. Örneğin self.assertIn ('Bu bozuk', context.exception) 2. Benim durumumda 2.7.10 kullanıldığında context.exception bir karakter dizisi gibi görünüyor. Str kullanmak işe yaramıyor. Bunu yaparak bitirdim: '' .join (context.exception) Yani, bir araya getirin: self.assertIn ('This is broken', '' .join (context.exception))
blockcipher

1
Metodunuzun test konsolunu istisnanın Geri İzlemesi ile tıkaması normal mi? Bunun olmasını nasıl önleyebilirim?
MadPhysicist

1
daha sonra iletiyi istisna str olarak almak için başka bir yol buldum, bu err = context.exception.message. Ve sonra testi yapmak için self.assertEqual (err, 'Bu bozuk') kullanın.
zhihong

326

Önceki cevabımdaki kod basitleştirilebilir:

def test_afunction_throws_exception(self):
    self.assertRaises(ExpectedException, afunction)

Ve eğer işlev argüman alırsa, onları assert'e aktarın.

def test_afunction_throws_exception(self):
    self.assertRaises(ExpectedException, afunction, arg1, arg2)

17
Argüman iletildiğinde ne yapılacağı hakkında ikinci sniper gerçekten yardımcı oldu.
Sabyasachi

Kullanıyorum 2.7.15. Eğer afunctioniçinde self.assertRaises(ExpectedException, afunction, arg1, arg2)sınıf başlatıcısı, sen geçmesi gerekiyor selfilk argüman ör gibi self.assertRaises(ExpectedException, Class, self, arg1, arg2)
Min Tran

128

Bir Python fonksiyonunun bir istisna verdiğini nasıl test edersiniz?

Nasıl bir işlev beklenen bir istisnayı atmazsa başarısız olan bir test nasıl yazılır?

Kısa cevap:

self.assertRaisesYöntemi bir bağlam yöneticisi olarak kullanın :

    def test_1_cannot_add_int_and_str(self):
        with self.assertRaises(TypeError):
            1 + '1'

gösteri

En iyi uygulama yaklaşımını bir Python kabuğunda göstermek oldukça kolaydır.

unittestkütüphane

Python 2.7 veya 3'te:

import unittest

Python 2.6'da, unittest2 unittestadlı 2.7'nin kütüphanesini ve sadece şu diğer adı içeren bir backport yükleyebilirsiniz unittest:

import unittest2 as unittest

Örnek testler

Şimdi, aşağıdaki Python'un tip güvenliği testini Python kabuğunuza yapıştırın:

class MyTestCase(unittest.TestCase):
    def test_1_cannot_add_int_and_str(self):
        with self.assertRaises(TypeError):
            1 + '1'
    def test_2_cannot_add_int_and_str(self):
        import operator
        self.assertRaises(TypeError, operator.add, 1, '1')

Test assertRaises, bir bağlam yöneticisi olarak kullanılır, bu da kaydedildiğinde hatanın düzgün bir şekilde yakalanmasını ve temizlenmesini sağlar.

İçerik yöneticisi olmadan da yazabiliriz , bkz. İkinci test. İlk argüman yükseltmeyi düşündüğünüz hata türü, ikinci argüman, test ettiğiniz fonksiyon ve kalan argümanlar ve anahtar kelime argümanları bu fonksiyona aktarılacaktır.

Sadece bağlam yöneticisini kullanmanın çok daha basit, okunabilir ve bakımı kolay olduğunu düşünüyorum.

Testlerin yapılması

Testleri yapmak için:

unittest.main(exit=False)

Python 2.6'da muhtemelen aşağıdakilere ihtiyacınız olacak :

unittest.TextTestRunner().run(unittest.TestLoader().loadTestsFromTestCase(MyTestCase))

Ve terminaliniz aşağıdakileri çıkarmalıdır:

..
----------------------------------------------------------------------
Ran 2 tests in 0.007s

OK
<unittest2.runner.TextTestResult run=2 errors=0 failures=0>

Ve beklediğimiz gibi, a 1ve bir '1'sonuç eklemeye çalıştığımızı görüyoruz TypeError.


Daha ayrıntılı çıktı için şunu deneyin:

unittest.TextTestRunner(verbosity=2).run(unittest.TestLoader().loadTestsFromTestCase(MyTestCase))

56

Kodunuz bu kalıba uymalıdır (bu bir unittest modülü stil testidir):

def test_afunction_throws_exception(self):
    try:
        afunction()
    except ExpectedException:
        pass
    except Exception:
       self.fail('unexpected exception raised')
    else:
       self.fail('ExpectedException not raised')

Python <2.7'de bu yapı, beklenen istisnada belirli değerleri kontrol etmek için kullanışlıdır. Unittest işlevi assertRaisesyalnızca bir istisnanın oluşup oluşmadığını kontrol eder.


3
ve yöntem self.fail sadece bir argüman alır
mdob

3
Bu, bir işlev bir istisna atarsa ​​test için aşırı derecede karmaşık görünüyor. Bu istisna dışında herhangi bir istisna testi hatalandıracağından ve bir istisna atmama testi başarısız olacağından, tek fark, farklı bir istisna assertRaisesalırsanız bir BAŞARISIZ yerine bir HATA alacağınızdır.
12'de unflores

23

from: http://www.lengrand.fr/2011/12/pythonunittest-assertraises-raises-error/

İlk olarak, dum_function.py dosyasındaki karşılık gelen (hala dum: p) işlevi şunlardır:

def square_value(a):
   """
   Returns the square value of a.
   """
   try:
       out = a*a
   except TypeError:
       raise TypeError("Input should be a string:")

   return out

Yapılacak test (sadece bu test eklenir):

import dum_function as df # import function module
import unittest
class Test(unittest.TestCase):
   """
      The class inherits from unittest
      """
   def setUp(self):
       """
       This method is called before each test
       """
       self.false_int = "A"

   def tearDown(self):
       """
       This method is called after each test
       """
       pass
      #---
         ## TESTS
   def test_square_value(self):
       # assertRaises(excClass, callableObj) prototype
       self.assertRaises(TypeError, df.square_value(self.false_int))

   if __name__ == "__main__":
       unittest.main()

Artık fonksiyonumuzu test etmeye hazırız! Testi çalıştırmaya çalışırken aşağıdakiler gerçekleşir:

======================================================================
ERROR: test_square_value (__main__.Test)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "test_dum_function.py", line 22, in test_square_value
    self.assertRaises(TypeError, df.square_value(self.false_int))
  File "/home/jlengrand/Desktop/function.py", line 8, in square_value
    raise TypeError("Input should be a string:")
TypeError: Input should be a string:

----------------------------------------------------------------------
Ran 1 test in 0.000s

FAILED (errors=1)

TypeError harekete geçirilir ve bir test hatası oluşturur. Sorun şu ki tam olarak istediğimiz davranış budur: s.

Bu hatayı önlemek için, test çağrısında lambda kullanarak işlevi çalıştırın:

self.assertRaises(TypeError, lambda: df.square_value(self.false_int))

Son çıktı:

----------------------------------------------------------------------
Ran 1 test in 0.000s

OK

Mükemmel !

... ve benim için de mükemmel !!

Çok fazla Thansk Bay Julien Lengrand-Lambert


Bu test iddiası aslında yanlış bir pozitif döndürür . Bunun nedeni, 'assertRaises' içindeki lambda'nın , test edilen işlevi değil , tür hatasını yükselten birim olmasıdır .


10
Sadece bir not, lambdaya ihtiyacın yok. Satır self.assertRaises(TypeError, df.square_value(self.false_int))yöntemi çağırır ve sonucu döndürür. İstediğiniz yöntemi ve herhangi bir argümanı geçmek ve self.assertRaises(TypeError, df.square_value, self.false_int)
unittest'in

Çok teşekkürler. mükemmel çalışıyor
Chandan Kumar

14

contextmanagerİstisnanın ortaya çıkıp çıkmadığını kontrol etmek için kendiniz bir tane oluşturabilirsiniz.

import contextlib

@contextlib.contextmanager
def raises(exception):
    try:
        yield 
    except exception as e:
        assert True
    else:
        assert False

Ve sonra şöyle kullanabilirsiniz raises:

with raises(Exception):
    print "Hola"  # Calls assert False

with raises(Exception):
    raise Exception  # Calls assert True

Eğer kullanıyorsanız pytest, bu şey zaten uygulanmıştır. Şunları yapabilirsiniz pytest.raises(Exception):

Misal:

def test_div_zero():
    with pytest.raises(ZeroDivisionError):
        1/0

Ve sonuç:

pigueiras@pigueiras$ py.test
================= test session starts =================
platform linux2 -- Python 2.6.6 -- py-1.4.20 -- pytest-2.5.2 -- /usr/bin/python
collected 1 items 

tests/test_div_zero.py:6: test_div_zero PASSED

1
unittestModülü gerektirmeyen bir cevap gönderdiğiniz için teşekkür ederiz !
Sherwood Callaway

10

Kullandığım doctest.testmodHemen her yerde [1] çünkü fonksiyonlarımı aynı anda belgelendirip test seviyorum.

Bu koda bir göz atın:

def throw_up(something, gowrong=False):
    """
    >>> throw_up('Fish n Chips')
    Traceback (most recent call last):
    ...
    Exception: Fish n Chips

    >>> throw_up('Fish n Chips', gowrong=True)
    'I feel fine!'
    """
    if gowrong:
        return "I feel fine!"
    raise Exception(something)

if __name__ == '__main__':
    import doctest
    doctest.testmod()

Bu örneği bir modüle koyar ve komut satırından çalıştırırsanız, her iki test durumu da değerlendirilir ve kontrol edilir.

[1] Python belgeleri: 23.2 doctest - Etkileşimli Python örneklerini test edin


4
Doctest'i seviyorum, ancak unittest'in yerine takviyeleri buluyorum.
TimothyAWiseman

2
Otomatik yeniden düzenleme ile doctest'in daha az oynama ihtimali daha düşük mü? Sanırım python için tasarlanan bir yeniden düzenleme aracı öğretilerden haberdar olmalı . Herkes deneyimlerinden yorum yapabilir mi?
kdbanman

6

Sadece Mock kütüphanesinin sadece beklenen istisnanın yükseltildiğini değil, aynı zamanda beklenen mesajla da yükseltildiğini kontrol edecek bir assertRaisesWithMessage () yöntemi (unittest.TestCase alt sınıfında) sağladığını keşfettim :

from testcase import TestCase

import mymod

class MyTestCase(TestCase):
    def test1(self):
        self.assertRaisesWithMessage(SomeCoolException,
                                     'expected message',
                                     mymod.myfunc)

Ne yazık ki, artık sağlamaz .. Ama @Art'ın yukarıdaki cevabı ( stackoverflow.com/a/3166985/1504046 ) aynı sonucu veriyor
Rmatt

6

Burada birçok cevap var. Kod, nasıl bir İstisna oluşturabileceğimizi, bu istisnayı yöntemlerimizde nasıl kullanabileceğimizi ve son olarak, bir birim testinde nasıl doğrulayabileceğinizi, doğru istisnaları ortaya koyduğunu gösterir.

import unittest

class DeviceException(Exception):
    def __init__(self, msg, code):
        self.msg = msg
        self.code = code
    def __str__(self):
        return repr("Error {}: {}".format(self.code, self.msg))

class MyDevice(object):
    def __init__(self):
        self.name = 'DefaultName'

    def setParameter(self, param, value):
        if isinstance(value, str):
            setattr(self, param , value)
        else:
            raise DeviceException('Incorrect type of argument passed. Name expects a string', 100001)

    def getParameter(self, param):
        return getattr(self, param)

class TestMyDevice(unittest.TestCase):

    def setUp(self):
        self.dev1 = MyDevice()

    def tearDown(self):
        del self.dev1

    def test_name(self):
        """ Test for valid input for name parameter """

        self.dev1.setParameter('name', 'MyDevice')
        name = self.dev1.getParameter('name')
        self.assertEqual(name, 'MyDevice')

    def test_invalid_name(self):
        """ Test to check if error is raised if invalid type of input is provided """

        self.assertRaises(DeviceException, self.dev1.setParameter, 'name', 1234)

    def test_exception_message(self):
        """ Test to check if correct exception message and code is raised when incorrect value is passed """

        with self.assertRaises(DeviceException) as cm:
            self.dev1.setParameter('name', 1234)
        self.assertEqual(cm.exception.msg, 'Incorrect type of argument passed. Name expects a string', 'mismatch in expected error message')
        self.assertEqual(cm.exception.code, 100001, 'mismatch in expected error code')


if __name__ == '__main__':
    unittest.main()

3

Unittest modülünden assertRaises kullanabilirsiniz

import unittest

class TestClass():
  def raises_exception(self):
    raise Exception("test")

class MyTestCase(unittest.TestCase):
  def test_if_method_raises_correct_exception(self):
    test_class = TestClass()
    # note that you dont use () when passing the method to assertRaises
    self.assertRaises(Exception, test_class.raises_exception)

-5

Tüm cevaplar gayet iyi olsa da, bir fonksiyonun birim test çerçevelerine dayanmadan ve test sınıfları yazmak zorunda kalmadan bir istisna getirip getirmediğini test etmenin bir yolunu arıyordum.

Sonunda aşağıdakileri yazdım:

def assert_error(e, x):
    try:
        e(x)
    except:
        return
    raise AssertionError()

def failing_function(x):
    raise ValueError()

def dummy_function(x):
    return x

if __name__=="__main__":
    assert_error(failing_function, 0)
    assert_error(dummy_function, 0)

Ve doğru yolda başarısız olur:

Traceback (most recent call last):
  File "assert_error.py", line 16, in <module>
    assert_error(dummy_function, 0)
  File "assert_error.py", line 6, in assert_error
    raise AssertionError()
AssertionError
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.