Frage

Wie schreibt man einen Unittest, der nur dann, wenn eine Funktion ausfällt nicht eine erwartete Ausnahme auslösen?

War es hilfreich?

Lösung

Verwenden TestCase.assertRaises (oder TestCase.failUnlessRaises) aus dem Modul Unittest zum Beispiel:

import mymod

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

Andere Tipps

Seit Python 2.7 Sie Kontext-Manager verwenden können ahold des eigentlichen Exception-Objekt zu erhalten geworfen:

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/ Bibliothek / unittest.html # unittest.TestCase.assertRaises


In Python 3.5 , haben Sie context.exception in str wickeln, sonst werden Sie eine TypeError bekommen

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

Der Code in meiner vorherigen Antwort kann vereinfacht werden:

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

Und wenn eine Funktion Argumente übernimmt, so dass sie nur in assertRaises passieren wie folgt aus:

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

Wie testen Sie, dass eine Python-Funktion löst eine Ausnahme?

     

Wie kann man einen Test schreiben, der nur schlägt fehl, wenn eine Funktion nicht ordnungsgemäß werfen   eine erwartete Ausnahme?

Kurze Antwort:

Mit der self.assertRaises Methode als Kontext-Manager:

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

Demonstration

Der Best-Practice-Ansatz ist ziemlich einfach, in einem Python-Shell zu demonstrieren.

Die unittest Bibliothek

In Python 2.7 oder 3:

import unittest

In Python 2.6 können Sie eine Rückportierung von 2,7 der unittest Bibliothek installieren, genannt unittest2 und nur alias dass als unittest:

import unittest2 as unittest

Beispiel Tests

Nun fügen Sie in Ihre Python-Shell den folgenden Test von Pythons Typsicherheit:

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 verwendet man assertRaises als Kontext-Manager, der dafür sorgt, dass der Fehler richtig aufgefangen und gereinigt, während aufgezeichnet.

Wir könnten es auch schreiben, ohne Kontext-Manager, siehe Test zwei. Das erste Argument wäre der Fehlertyp, den Sie zu erhöhen erwarten, das zweite Argument, die Funktion Sie testen, und die restlichen args und Keyword-args wird an diese Funktion übergeben werden.

Ich denke, es ist viel einfacher, lesbar und wartbar nur den Kontext-Manager zu verwenden.

Ausführen der Tests

, um die Tests auszuführen:

unittest.main(exit=False)

In Python 2.6, werden Sie wahrscheinlich benötigen folgende :

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

Und Ihr Terminal ausgeben soll wie folgt vor:

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

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

Und wir sehen, dass, wie wir erwarten, versuchen, ein 1 und ein '1' Ergebnis in einem TypeError hinzuzufügen.


Für weitere ausführliche Ausgabe, versuchen Sie dies:

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

Der Code soll dieses Muster folgen (dies ist ein Unittest-Modul-Test):

def test_afunction_throws_exception(self):
    try:
        afunction()
    except ExpectedException:
        pass
    except Exception as e:
       self.fail('Unexpected exception raised:', e)
    else:
       self.fail('ExpectedException not raised')

Ein Python <2.7 dieses Konstrukt zur Überprüfung nach bestimmten Werten in der erwarteten Ausnahme nützlich ist. Die Unittest-Funktion assertRaises prüft nur, wenn eine Ausnahme ausgelöst wurde.

aus: http://www.lengrand.fr/2011 / 12 / pythonunittest-assertraises-raist-Fehler /

Als erstes ist hier die entsprechende (noch dum: p) Funktion in der Datei dum_function.py:

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

Hier ist der Test durchgeführt werden soll (nur dieser Test eingesetzt wird):

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

Wir sind nun bereit, unsere Funktion zu testen! Hier ist, was passiert, wenn man versucht, den Test auszuführen:

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

Das ist Typeerror actullay erhöht und erzeugt ein Testversagen. Das Problem ist, dass dies genau das Verhalten, das wir wollten: s.

Um diesen Fehler zu vermeiden, führen Sie einfach die Funktion Lambda im Testanruf mit:

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

Die endgültige Ausgabe:

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

OK

Perfect!

... und ist perfekt für mich auch !!

Thansk viel Herr Julien Lengrand-Lambert

Sie können Ihre eigenen contextmanager zu überprüfen, ob die Exception ausgelöst wurde bauen.

import contextlib

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

Und dann können Sie raises wie folgt verwenden:

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

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

Wenn Sie pytest verwenden, wird diese Sache bereits umgesetzt. Sie können pytest.raises(Exception) tun:

Beispiel:

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

Und das Ergebnis:

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

ich doctest [1] fast überall, weil ich mag die Tatsache, dass ich dokumentieren und testen Sie meine Funktionen zur gleichen Zeit.

Haben Sie einen Blick auf diesen Code:

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

Wenn Sie dieses Beispiel in einem Modul setzen und es von der Kommandozeile beiden Testfälle ausgeführt werden ausgewertet und überprüft.

[1] Python-Dokumentation: 23,2 doctest - Test interaktiver Python Beispiele

Haben Sie einen Blick auf die assertRaises Methode das unittest Modul.

Ich habe gerade entdeckt, dass die Mock Bibliothek eine assertRaisesWithMessage () -Methode liefert ( in seiner unittest.TestCase Unterklasse), die nicht nur, dass die erwartete Ausnahme ausgelöst wird überprüft, sondern auch, dass es mit der erwarteten Nachricht ausgelöst wird:

from testcase import TestCase

import mymod

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

Sie können assertRaises vom Unittest-Modul verwenden

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)

Es gibt hier viele Antworten. Der Code zeigt, wie wir eine Ausnahme erstellen können, wie wir diese Ausnahme in unseren Methoden verwenden können, und schließlich, wie Sie in einem Unittest überprüfen können, die richtigen Ausnahmen angehoben werden.

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()
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top