2016-05-10 2 views
8

Ich habe einige widersprüchliche Hinweise zur Verwendung von assert in der setUp Methode eines Python-Unit-Tests gelesen. Ich kann den Schaden nicht sehen, der einen Test versagt, wenn eine Vorbedingung, die auf Test beruht, ausfällt.Wie kann man einen Fehler in einem Python-Komponententest in der setUp-Methode korrekt melden?

Zum Beispiel:

import unittest 

class MyProcessor(): 
    """ 
    This is the class under test 
    """ 

    def __init__(self): 
     pass 

    def ProcessData(self, content): 
     return ['some','processed','data','from','content'] # Imagine this could actually pass 

class Test_test2(unittest.TestCase): 

    def LoadContentFromTestFile(self): 
     return None # Imagine this is actually doing something that could pass. 

    def setUp(self): 
     self.content = self.LoadContentFromTestFile() 
     self.assertIsNotNone(self.content, "Failed to load test data") 
     self.processor = MyProcessor() 

    def test_ProcessData(self): 
     results = self.processor.ProcessData(self.content) 
     self.assertGreater(results, 0, "No results returned") 

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

Dies scheint eine vernünftige Sache zu mir tun heißen sicherzustellen, dass der Test in der Lage zu laufen. Wenn dies aufgrund der Setup-Zustand nicht erhalten wir:

F 
====================================================================== 
FAIL: test_ProcessData (__main__.Test_test2) 
---------------------------------------------------------------------- 
Traceback (most recent call last): 
    File "C:\Projects\Experiments\test2.py", line 21, in setUp 
    self.assertIsNotNone(self.content, "Failed to load test data") 
AssertionError: unexpectedly None : Failed to load test data 

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

FAILED (failures=1) 

Antwort

9

Der Zweck setUp ist Boilerplate code zu reduzieren, die zwischen den Tests schafft in der Testklasse während der Arrange-Phase

in der Arrange-Phase Sie:.. Setup alles für den Lauf des getesteten Code benötigt Dazu gehört alle Initialisierung von Abhängigkeiten, spotten und Daten für den Test benötigten zu laufen

. Basierend auf den obigen Absätzen sollten Sie nichts in Ihrer setUp Methode geltend machen.

Also wie bereits erwähnt; Wenn Sie die Testvoraussetzung nicht erstellen können, ist Ihr Test fehlerhaft. Um solche Situationen zu vermeiden, schrieb Roy Osherove ein großes Buch mit dem Titel: The Art Of Unit Testing (Für eine vollständige Enthüllung Lior Friedman (Er war Roy's Chef) ist ein Freund von mir und ich arbeitete eng mit ihnen für mehr als 2 Jahre, also bin ich ein bisschen verzerrt ...)

Grundsätzlich gibt es nur ein paar Gründe für eine Interaktion mit externen Ressourcen während der Arrangierphase (oder mit Dingen, die eine Ausnahme verursachen können), die meisten von ihnen (wenn nicht alle) sind verwandt in Integrationstests.

Zurück zu Ihrem Beispiel; Es gibt ein Muster, um die Tests zu strukturieren, wobei Sie eine externe Ressource (für alle/die meisten von ihnen) laden müssen. Nur eine Randnotiz; Bevor Sie sich entscheiden, dieses Muster anzuwenden, stellen Sie sicher, dass Sie diesen Inhalt nicht als statische Ressource in Ihrer UT-Klasse haben können, wenn andere Testklassen diese Ressource verwenden müssen, um diese Ressource in ein Modul zu extrahieren.

folgendes Schema die Möglichkeit für Fehler verringern, da Sie wenige Anrufe an die externen Ressource:

class TestClass(unittest.TestCase): 

    def setUpClass(self): 
     # since external resources such as other servers can provide a bad content 
     # you can verify that the content is valid 
     # then prevent from the tests to run 
     # however, in most cases you shouldn't. 
     self.externalResourceContent = loadContentFromExternalResource() 


    def setUp(self): 
     self.content = self.copyContentForTest() 

Vorteile:

  1. weniger Chancen zum Scheitern
  2. Inkonsistenz Verhalten verhindern (1 . irgendwas/one hat die externe Ressource bearbeitet 2. Sie haben die externe Ressource in einigen Ihrer Tests nicht geladen)
  3. schnellere Ausführung

Nachteile:

  1. der Code ist komplexer
5

setUp ist nicht für behaupten Voraussetzungen aber Schaffung sie. Wenn Ihr Test die erforderliche Fixture nicht erstellen kann, ist sie defekt und nicht fehlerhaft.

+0

So, wie Sie das in einer automatisierten Build-Umgebung hervorheben sollen? –

+3

@JonCage IMHO Wenn Sie wirklich Vorbedingungen testen müssen, sollten Sie einen Test für sie schreiben und 'setUp()' nicht verwenden, um zu bestätigen, dass Ihre Vorbedingungen gültig sind. Fügen Sie also einen Test wie 'test_load_fixture (self): self.assertIsNotNone (self.content," Fehler beim Laden von Testdaten ")' hinzu und Sie erhalten einen Fehler für diesen Test und einen Fehler für jeden Test, der 'self.content verwendet '. –

2

Hier gibt es keine richtige oder falsche Antwort, es hängt davon ab, was Sie testen und wie teuer Ihre Tests sind. Einige Tests sind zu gefährlich, um versuchte Läufe zuzulassen, wenn die Daten nicht wie erwartet sind, einige müssen mit diesen Daten arbeiten.

Sie können Assertionen in setUp verwenden, wenn Sie zwischen Tests auf bestimmte Bedingungen prüfen müssen. Dies kann dazu beitragen, wiederholten Code in Ihren Tests zu reduzieren. Aber auch bewegte Testmethoden zwischen Klassen oder Dateien werden etwas komplizierter, da sie auf das äquivalente setUp angewiesen sind. Es kann auch die Grenzen der Komplexität für weniger Code-versierte Tester verschieben.

Es ist ein wenig sauberer, einen Test zu haben, der diese Startbedingungen einzeln überprüft und zuerst ausführt, sie werden zwischen den einzelnen Tests möglicherweise nicht benötigt. Wenn Sie es als test_01_check_preconditions definieren, wird es vor allen anderen Testmethoden ausgeführt, auch wenn der Rest zufällig ist. Sie können dann unittest2.skip Dekoratoren für bestimmte Bedingungen verwenden. Ein besserer Ansatz besteht darin, addCleanup zu verwenden, um sicherzustellen, dass der Status zurückgesetzt wird. Der Vorteil besteht darin, dass selbst bei fehlgeschlagenem Test die Bereinigung besser auf die spezifische Situation bei der Definition ausgerichtet wird der Kontext Ihrer Testmethode.

Es gibt auch nichts, was Sie daran hindert, Methoden zu definieren, um allgemeine Prüfungen in der Unittest-Klasse durchzuführen und sie in setUp oder in test_methods aufzurufen. Dies kann dazu beitragen, die Komplexität in definierten und verwalteten Bereichen zu halten.

Auch nicht versucht werden, unittest2 über eine einfache Test-Definition zu untergliedern, ich habe gesehen, Menschen versuchen, das zu tun, um Tests einfach und tatsächlich völlig unerwartet Verhalten einzuführen.

Ich denke, die echte nach Hause nehmen ist, wenn Sie es wissen, warum Sie es verwenden möchten und sicherstellen, dass Sie Ihre Gründe dokumentieren, ist es wahrscheinlich in Ordnung, wenn Sie unsicher sind dann gehen Sie für die einfachste leicht zu verstehen Option, weil Tests sind nutzlos, wenn sie nicht einfach zu verstehen sind.

3

Vom Python Standard Library Documentation:

„Wenn das Setup() -Methode eine Ausnahme auslöst, während der Test läuft, den Rahmen des Tests prüfen, einen Fehler gelitten zu haben, und die runTest() -Methode Wenn setUp() erfolgreich war, wird die Methode tearDown() ausgeführt, unabhängig davon, ob runTest() erfolgreich war oder nicht: Eine Arbeitsumgebung für den Testcode heißt Fixture."

Eine Assertion Ausnahme im setUp() Methode würde durch die Unittest Rahmen als Fehler betrachtet werden. Der Test wird nicht ausgeführt.

2

Es gibt einen Grund, warum Sie Behauptungen in einem setUp() vermeiden wollen. Wenn setUp fehlschlägt, wird Ihr tearDown nicht ausgeführt.

Wenn Sie zum Beispiel einen Satz von Datenbankeinträgen einrichten und Ihr Teardown diese Datensätze löscht, werden diese Datensätze nicht gelöscht.

Mit diesem Code-Schnipsel:

import unittest 

class Test_test2(unittest.TestCase): 

    def setUp(self): 
     print 'setup' 
     assert False 

    def test_ProcessData(self): 
     print 'testing' 

    def tearDown(self): 
     print 'teardown' 

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

Sie führen nur die setUp():

$ python t.py 
setup 
E 
====================================================================== 
ERROR: test_ProcessData (__main__.Test_test2) 
---------------------------------------------------------------------- 
Traceback (most recent call last): 
    File "t.py", line 7, in setUp 
    assert False 
AssertionError 

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

FAILED (errors=1) 
Verwandte Themen