wake-up-neo.com

Wie zeige ich die Fehlermeldungen von assertRaises () in unittest in Python2.7 an?

Um sicherzustellen, dass die Fehlermeldungen meines Moduls informativ sind, möchte ich alle Fehlermeldungen sehen, die von assertRaises () abgefangen wurden. Heute mache ich das für jeden assertRaises (), aber da es viele davon im Testcode gibt, wird es sehr mühsam.

Wie kann ich die Fehlermeldungen für alle assertRaises () ausdrucken? Ich habe die Dokumentation zu http: //docs.python.org/library/unittest.html studiert, ohne herauszufinden, wie man es löst. Kann ich die assertRaises () -Methode irgendwie monkeypatchen? Ich ziehe es vor, nicht alle assertRaises () -Zeilen im Testcode zu ändern, da ich den Testcode am häufigsten auf die übliche Weise verwende.

Ich vermute, diese Frage bezieht sich auf Python unittest: Wie teste ich das Argument in einer Exceptions?

So mache ich es heute. Beispielsweise:

#!/usr/bin/env python

def fail():
    raise ValueError('Misspellled errrorr messageee')

Und der Testcode:

#!/usr/bin/env python
import unittest
import failure   

class TestFailureModule(unittest.TestCase):

    def testFail(self):
        self.assertRaises(ValueError, failure.fail)

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

Um die Fehlermeldung zu überprüfen, ändere ich einfach den Fehlertyp in assertRaises () in zum Beispiel IOError. Dann kann ich die Fehlermeldung sehen:

 E
======================================================================
ERROR: testFail (__main__.TestFailureModule)
----------------------------------------------------------------------
Traceback (most recent call last):
 File "test_failure.py", line 8, in testFail
   self.assertRaises(IOError, failure.fail)
  File "/usr/lib/python2.7/unittest/case.py", line 471, in assertRaises
    callableObj(*args, **kwargs)
 File "/home/jonas/Skrivbord/failure.py", line 4, in fail
    raise ValueError('Misspellled errrorr messageee')
ValueError: Misspellled errrorr messageee

----------------------------------------------------------------------
Ran 1 test in 0.001s

FAILED (errors=1)

Irgendwelche Vorschläge?/Jonas

EDIT:

Mit den Hinweisen von Robert Rossney konnte ich das Problem lösen. Es ist nicht hauptsächlich für Rechtschreibfehler gedacht, sondern um sicherzustellen, dass die Fehlermeldungen für den Benutzer des Moduls wirklich aussagekräftig sind. Die normale Funktionalität von unittest (so benutze ich es die meiste Zeit) wird durch Setzen von SHOW_ERROR_MESSAGES = False erreicht.

Ich überschreibe einfach die assertRaises () -Methode (siehe unten). Es funktioniert wie Charme!

SHOW_ERROR_MESSAGES = True

class NonexistantError(Exception):
    pass

class ExtendedTestCase(unittest.TestCase):
    def assertRaises(self, excClass, callableObj, *args, **kwargs):
        if SHOW_ERROR_MESSAGES:
            excClass = NonexistantError
        try:
            unittest.TestCase.assertRaises(self, excClass, callableObj, *args, **kwargs)
        except:
            print '\n    ' + repr(sys.exc_info()[1]) 

Ein Bruchteil der resultierenden Ausgabe:

testNotIntegerInput (__main__.TestCheckRegisteraddress) ... 
    TypeError('The registeraddress must be an integer. Given: 1.0',)

    TypeError("The registeraddress must be an integer. Given: '1'",)

    TypeError('The registeraddress must be an integer. Given: [1]',)

    TypeError('The registeraddress must be an integer. Given: None',)
ok
testCorrectNumberOfBytes (__main__.TestCheckResponseNumberOfBytes) ... ok
testInconsistentLimits (__main__.TestCheckNumerical) ... 
    ValueError('The maxvalue must not be smaller than minvalue. Given: 45 and 47, respectively.',)

    ValueError('The maxvalue must not be smaller than minvalue. Given: 45.0 and 47.0, respectively.',)
ok
testWrongValues (__main__.TestCheckRegisteraddress) ... 
    ValueError('The registeraddress is too small: -1, but minimum value is 0.',)

    ValueError('The registeraddress is too large: 65536, but maximum value is 65535.',)
ok
testTooShortString (__main__.TestCheckResponseWriteData) ... 
    ValueError("The payload is too short: 2, but minimum value is 4. Given: '\\x00X'",)

    ValueError("The payload is too short: 0, but minimum value is 4. Given: ''",)

    ValueError("The writedata is too short: 1, but minimum value is 2. Given: 'X'",)

    ValueError("The writedata is too short: 0, but minimum value is 2. Given: ''",)
ok
testKnownValues (__main__.TestCreateBitPattern) ... ok
testNotIntegerInput (__main__.TestCheckSlaveaddress) ... 
    TypeError('The slaveaddress must be an integer. Given: 1.0',)

    TypeError("The slaveaddress must be an integer. Given: '1'",)

    TypeError('The slaveaddress must be an integer. Given: [1]',)

    TypeError('The slaveaddress must be an integer. Given: None',)
ok
66
jonasberg

Out-of-the-Box unittest macht das nicht. Wenn Sie dies häufig tun möchten, können Sie Folgendes versuchen:

class ExtendedTestCase(unittest.TestCase):

  def assertRaisesWithMessage(self, msg, func, *args, **kwargs):
    try:
      func(*args, **kwargs)
      self.assertFail()
    except Exception as inst:
      self.assertEqual(inst.message, msg)

Leiten Sie Ihre Unit-Test-Klassen von ExtendedTestCase statt von unittest.TestCase Ab.

Aber wenn Sie sich nur um falsch geschriebene Fehlermeldungen sorgen und Testfälle erstellen möchten, sollten Sie Nachrichten nicht als Zeichenfolgenliterale einfügen. Sie sollten mit ihnen tun, was Sie mit anderen wichtigen Zeichenfolgen tun: Definieren Sie sie als Konstanten in einem Modul, das Sie importieren, und dass jemand für das Korrekturlesen verantwortlich ist. Ein Entwickler, der Wörter in seinem Code falsch schreibt, schreibt sie auch in seinen Testfällen falsch.

44
Robert Rossney

Ich habe einmal die beste Antwort von @Robert Rossney vorgezogen. Heutzutage bevorzuge ich assertRaises als Kontextmanager (eine neue Funktion in unittest2) wie folgt:

with self.assertRaises(TypeError) as cm:
    failure.fail()
self.assertEqual(
    'The registeraddress must be an integer. Given: 1.0',
    str(cm.exception)
)
102
mkelley33

Sie suchen nach assertRaisesRegexp , das verfügbar ist seit Python 2.7. Aus den Dokumenten:

self.assertRaisesRegexp(ValueError, 'invalid literal for.*XYZ$', int, 'XYZ')

oder:

with self.assertRaisesRegexp(ValueError, 'literal'):
    int('XYZ')
50
Iodnas

Wenn Sie möchten, dass die Fehlermeldung genau mit etwas übereinstimmt:

with self.assertRaises(ValueError) as error:
  do_something()
self.assertEqual(error.exception.message, 'error message')
22
yalei du

mkelley33 gibt eine gute Antwort, aber dieser Ansatz kann von einigen Code-Analyse-Tools wie Codacy als Problem erkannt werden. Das Problem ist, dass es nicht weiß, dass assertRaises als Kontextmanager verwendet werden kann, und dass nicht alle Argumente an die assertRaises-Methode übergeben werden .

Also, ich möchte Roberts Rossney-Antwort verbessern:

class TestCaseMixin(object):

    def assertRaisesWithMessage(self, exception_type, message, func, *args, **kwargs):
        try:
            func(*args, **kwargs)
        except exception_type as e:
            self.assertEqual(e.args[0], message)
        else:
            self.fail('"{0}" was expected to throw "{1}" exception'
                      .format(func.__name__, exception_type.__name__))

Wichtige Unterschiede sind:

  1. Wir testen die Art der Ausnahme.
  2. Wir können diesen Code sowohl auf Python 2 als auch Python 3 (wir nennen e.args[0] weil Fehler in Py3 kein message Attribut haben).
5
oblalex