wake-up-neo.com

Unittest Tests bestellen

Wie kann ich sicher sein, dass die besten Methoden bestellt werden? Sind die alphabetischen oder numerischen Präfixe richtig?

class TestFoo(TestCase):
    def test_1(self):
        ...
    def test_2(self):
        ...

oder

class TestFoo(TestCase):
    def test_a(self):
        ...
    def test_b(self):
        ...
36
nmb.ten

Sie können es deaktivieren, indem Sie sortTestMethodsUsing auf None festlegen: http://docs.python.org/2/library/unittest.html#unittest.TestLoader.sortTestMethodsUsing

Bei reinen Einsätzen haben Sie recht; aber für Komponententests und Integrationstests ..... Ich stimme nicht zu, dass Sie nichts über den Zustand annehmen sollen. Was ist, wenn Sie den Zustand testen. Wenn Ihr Test beispielsweise bestätigt, dass ein Dienst ist Bei der Installation automatisch gestartet. Wenn Sie in Ihrem Setup den Dienst starten, dann die Assertion durchführen, testen Sie den Status nicht mehr, Sie testen jedoch die Funktion "Dienststart".

Ein anderes Beispiel ist, wenn Ihr Setup sehr lange dauert oder viel Platz benötigt und es unpraktisch wird, das Setup häufig auszuführen.

Viele Entwickler neigen dazu, "unittest" -Frameworks für Komponententests zu verwenden ... also hören Sie auf und fragen Sie sich, mache ich Unittest- oder Komponententests.

58
max

Es gibt keinen Grund dafür, dass Sie nicht auf das aufbauen können, was in einem vorherigen Test gemacht wurde, oder es für den nächsten Test von Grund auf neu erstellen sollte. Zumindest wird normalerweise kein Grund angegeben, aber stattdessen sagen die Leute einfach "Sie sollten nicht". Das ist nicht hilfreich.

Im Allgemeinen bin ich es leid, zu viele Antworten hier zu lesen, die im Wesentlichen sagen "Sie sollten das nicht tun", anstatt irgendwelche Informationen darüber zu geben, wie dies am besten zu tun ist, wenn es nach Meinung der Fragesteller einen guten Grund dafür gibt. Wenn ich die Meinung von jemandem wollte, ob ich etwas tun sollte, hätte ich um Meinungen gefragt, ob es eine gute Idee ist. 

Wenn Sie so lesen, sagen Sie, dass loadTestsFromTestCase und wie es heißt, letztendlich nach Methoden mit einem bestimmten Namensmuster in der Reihenfolge durchsucht wird, in der sie im Klassenmethodenverzeichnis vorkommen, also grundsätzlich in der Schlüsselreihenfolge. Es nimmt diese Informationen und erstellt eine Testsuite, um sie der TestCase-Klasse zuzuordnen. Wenn Sie stattdessen eine Liste angeben, die nach Ihren Wünschen sortiert ist, können Sie dies tun. Ich bin mir nicht so sicher, was die effizienteste/sauberste Methode ist, aber das funktioniert. 

67
Seren Seraph

Wenn Sie 'nose' verwenden und Ihre Testfälle als Funktionen schreiben (und nicht als Methoden einer von TestCase abgeleiteten Klasse), fälscht 'nose' nicht die Reihenfolge, sondern verwendet die Reihenfolge der Funktionen, die in der Datei definiert ist. Um die assert_ * -Methoden griffbereit zu haben, ohne TestCase zu unterteilen, verwende ich normalerweise das Testmodul von numpy. Beispiel: 

from numpy.testing import *

def test_aaa():
    assert_equal(1, 1)

def test_zzz():
    assert_equal(1, 1)

def test_bbb():
    assert_equal(1, 1)

Das Ausführen mit '' nosetest -vv '' ergibt:

test_it.test_aaa ... ok
test_it.test_zzz ... ok
test_it.test_bbb ... ok
----------------------------------------------------------------------
Ran 3 tests in 0.050s
OK

Beachten Sie für alle, die behaupten, dass Komponententests nicht angeordnet werden sollten: Zwar sollten Komponententests isoliert sein und unabhängig voneinander ausgeführt werden können, Ihre Funktionen und Klassen sind jedoch normalerweise nicht unabhängig. Sie bauen sich eher auf, von einfacheren Funktionen/Funktionen auf niedrigerer Ebene zu komplexeren Funktionen/Funktionen auf höherer Ebene. Wenn Sie anfangen, Ihre Low-Level-Funktionen zu optimieren und durcheinander zu bringen (für meinen Teil mache ich das häufig; wenn Sie dies nicht tun, benötigen Sie wahrscheinlich sowieso keinen Unit-Test ;-), dann ist es viel besser für die Diagnose der Ursache. Wenn die Tests für einfache Funktionen an erster Stelle stehen, und Tests auf Funktionen, die später von diesen Funktionen abhängen. Wenn die Tests alphabetisch sortiert werden, wird die eigentliche Ursache in der Regel von hundert fehlgeschlagenen Assertions ertränkt, die nicht vorhanden sind, weil die zu testende Funktion einen Fehler aufweist, sondern weil die Low-Level-Funktion, auf die sie sich bezieht, eine Rolle spielt.

Deshalb möchte ich, dass meine Komponententests so sortiert werden, wie ich sie spezifiziert habe: In früheren Tests nicht den in früheren Tests aufgebauten Status zu verwenden, sondern als sehr hilfreiches Werkzeug bei der Diagnose von Problemen.

12
Elmar Zander

Warum benötigen Sie einen bestimmten Testauftrag? Die Tests sollten isoliert sein und sollten daher in beliebiger Reihenfolge oder sogar parallel ausgeführt werden können.

Wenn Sie beispielsweise einen Benutzer abmelden möchten, kann der Test eine neue Datenbank mit einem Testabonnement erstellen und dann versuchen, ihn abzumelden. Dieses Szenario hat seine eigenen Probleme, aber am Ende ist es besser als Tests von einander abhängig zu sein. (Beachten Sie, dass Sie gängigen Testcode herausfiltern können, sodass Sie den DB-Setup-Code nicht wiederholen oder Testdaten erstellen müssen.)

12
zoul

Ich bin mit der Idee, dass Tests nicht bestellt werden sollten, nur halbwegs einverstanden. In manchen Fällen hilft es (es ist einfacher, verdammt noch mal!), Sie in der richtigen Reihenfolge zu haben.

Eine Alternative ist die Verwendung von Mock-Objekten, um die Objekte, die ausgeführt werden sollen, vor dem betreffenden getesteten Code zu simulieren und zu patchen. Sie können auch eine Dummy-Funktion einfügen, um Ihren Code zu patchen. Weitere Informationen finden Sie unter Mock, das jetzt Bestandteil der Standardbibliothek ist . Mock

Hier sind einige YouTube-Videos, wenn Sie Mock noch nicht verwendet haben.

Video 1

Video 2

Video 3

Um es auf den Punkt zu bringen: Versuchen Sie, Ihren Code mit Klassenmethoden zu strukturieren, und platzieren Sie dann alle Klassenmethoden in einer Haupttestmethode.

import unittest
import sqlite3

class MyOrderedTest(unittest.TestCase):

    @classmethod
    def setUpClass(cls):
        cls.create_db()
        cls.setup_draft()
        cls.draft_one()
        cls.draft_two()
        cls.draft_three()

    @classmethod
    def create_db(cls):
        cls.conn = sqlite3.connect(":memory:")

    @classmethod
    def setup_draft(cls):
        cls.conn.execute("CREATE TABLE players ('draftid' INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, 'first', 'last')")

    @classmethod
    def draft_one(cls):
        player = ("Hakeem", "Olajuwon")
        cls.conn.execute("INSERT INTO players (first, last) VALUES (?, ?)", player)

    @classmethod
    def draft_two(cls):
        player = ("Sam", "Bowie")
        cls.conn.execute("INSERT INTO players (first, last) VALUES (?, ?)", player)

    @classmethod
    def draft_three(cls):
        player = ("Michael", "Jordan")
        cls.conn.execute("INSERT INTO players (first, last) VALUES (?, ?)", player)

    def test_unordered_one(self):
        cur = self.conn.execute("SELECT * from players")
        draft = [(1, u'Hakeem', u'Olajuwon'), (2, u'Sam', u'Bowie'), (3, u'Michael', u'Jordan')]
        query = cur.fetchall()
        print query
        self.assertListEqual(query, draft)

    def test_unordered_two(self):
        cur = self.conn.execute("SELECT first, last FROM players WHERE draftid=3")
        result = cur.fetchone()
        third = " ".join(result)
        print third
        self.assertEqual(third, "Michael Jordan")
8
Jason Wirth

Verlassen Sie sich nicht auf die Bestellung. Wenn sie einen allgemeinen Status wie das Dateisystem oder die Datenbank verwenden, sollten Sie setUp- und tearDown-Methoden erstellen, die Ihre Umgebung in einen testfähigen Zustand versetzen, und dann nach dem Ausführen der Tests bereinigen. Bei jedem Test sollte davon ausgegangen werden, dass die Umgebung wie in setUp definiert ist, und keine weiteren Annahmen treffen.

7
user23743

Es gibt eine Reihe von Gründen für die Priorisierung von Tests, nicht zuletzt die Produktivität, auf die JUnit Max abzielt. Es ist manchmal hilfreich, sehr langsame Tests in einem eigenen Modul zu speichern, damit Sie schnell ein Feedback von den Tests erhalten können, die nicht unter den gleichen starken Abhängigkeiten leiden. Die Bestellung ist auch hilfreich, um Fehler aus Tests zu ermitteln, die nicht vollständig abgeschlossen sind.

6
eradman

Ok, kann etwas später sein, aber trotzdem ...

Versuchen Sie es mit proboscis library. Es ermöglicht Ihnen, Testreihenfolgen zu erstellen sowie Testabhängigkeiten einzurichten. Ich benutze es und diese Bibliothek ist wirklich großartig. 

Wenn beispielsweise test case #1 von module A von test case #3 von module B abhängen soll, legen Sie CAN dieses Verhalten fest Benutzung der Bibliothek.

6
gahcep

http://docs.python.org/library/unittest.html

Beachten Sie, dass die Reihenfolge, in der die verschiedenen Testfälle ausgeführt werden, durch das Sortieren der Testfunktionsnamen in Bezug auf die integrierte Reihenfolge von Zeichenfolgen bestimmt wird.

Wenn Sie die Set-Reihenfolge explizit benötigen, verwenden Sie einen monolithischen Test. 

class Monolithic(TestCase):
  def step1(self):
      ...

  def step2(self):
      ...

  def steps(self):
    for name in sorted(dir(self)):
      if name.startswith("step"):
        yield name, getattr(self, name) 

  def test_steps(self):
    for name, step in self.steps():
      try:
        step()
      except Exception as e:
        self.fail("{} failed ({}: {})".format(step, type(e), e)

Check out post für Details.

4
morsik

Es gibt Szenarien, in denen die Reihenfolge wichtig sein kann und SetUp und Teardown begrenzt erscheinen. Es gibt nur eine setUp- und tearDown-Methode, die logisch ist, aber Sie können nur so viele Informationen hinzufügen, bis unklar wird, was setUp oder tearDown tatsächlich tun wird.

Nehmen Sie diesen Integrationstest als Beispiel:

Sie schreiben Tests, um zu sehen, ob das Registrierungsformular und das Anmeldeformular korrekt funktionieren. In diesem Fall ist die Bestellung wichtig, da Sie sich ohne ein bestehendes Konto nicht anmelden können. Noch wichtiger ist, dass die Reihenfolge Ihrer Tests eine Art Benutzerinteraktion darstellt. Wo jeder Test einen Schritt im gesamten Prozess oder Ablauf darstellt, den Sie testen.

Die Aufteilung des Codes in diese logischen Teile hat mehrere Vorteile. 

Vielleicht nicht die beste Lösung, aber ich benutze oft eine Methode, mit der die eigentlichen Tests beginnen. 

def test_registration_login_flow(self):
    _test_registration_flow()
    _test_login_flow()
4
Jonas Geiregat

Eine einfache Methode zum Bestellen "unittest" -Tests besteht darin, dem init.d-Mechanismus zu folgen, um ihnen numerische Namen zu geben:

def test_00_createEmptyObject(self):
    obj = MyObject()
    self.assertIsEqual(obj.property1, 0)
    self.assertIsEqual(obj.dict1, {})

def test_01_createObject(self):
    obj = MyObject(property1="hello", dict1={"pizza":"pepperoni"})
    self.assertIsEqual(obj.property1, "hello")
    self.assertIsDictEqual(obj.dict1, {"pizza":"pepperoni"})

def test_10_reverseProperty(self):
    obj = MyObject(property1="world")
    obj.reverseProperty1()
    self.assertIsEqual(obj.property1, "dlrow")

In solchen Fällen sollten Sie jedoch in Betracht ziehen, Ihre Tests anders zu strukturieren, um auf früheren Konstruktionsfällen aufbauen zu können. Zum Beispiel könnte es im obigen Sinn sinnvoll sein, über eine Funktion "Konstruieren und Nutzen" zu verfügen, die das Objekt konstruiert und dessen Parametrierung überprüft.

def make_myobject(self, property1, dict1):  # Must be specified by caller
    obj = MyObject(property1=property1, dict1=dict1)
    if property1:
        self.assertEqual(obj.property1, property1)
    else:
        self.assertEqual(obj.property1, 0)
    if dict1:
        self.assertDictEqual(obj.dict1, dict1)
    else:
        self.assertEqual(obj.dict1, {})
    return obj

def test_00_createEmptyObject(self):
    obj = self.make_object(None, None)

def test_01_createObject(self):
    obj = self.make_object("hello", {"pizza":"pepperoni"})

def test_10_reverseProperty(self):
    obj = self.make_object("world", None)
    obj.reverseProperty()
    self.assertEqual(obj.property1, "dlrow")
3
kfsone

Ich stimme der Aussage zu, dass die Antwort einer Decke "nicht das tun" eine schlechte Antwort ist. 

Ich habe eine ähnliche Situation, in der ich eine einzige Datenquelle habe und ein Test den Datensatz löscht, wodurch andere Tests fehlschlagen.

Meine Lösung bestand darin, die Betriebssystemumgebungsvariablen in meinem Bamboo-Server zu verwenden ...

(1) Der Test für die "Datenbereinigung" -Funktionalität beginnt mit einer while-Schleife, die den Status einer Umgebungsvariablen "BLOCK_DATA_PURGE" überprüft. Wenn die Variable "BLOCK_DATA_PURGE" größer als Null ist, schreibt die Schleife einen Protokolleintrag mit dem Effekt, dass sie 1 Sekunde schläft. Sobald "BLOCK_DATA_PURGE" einen Wert von Null hat, wird mit der Ausführung die Bereinigungsfunktion getestet.

(2) Jeder Unit-Test, der die Daten in der Tabelle benötigt, erhöht am Anfang (in setup ()) einfach "BLOCK_DATA_PURGE" und dekrementiert dieselbe Variable in teardown (). 

Dies hat zur Folge, dass verschiedene Datenverbraucher die Bereinigungsfunktion so lange blockieren können, wie sie es brauchen, ohne befürchten zu müssen, dass die Bereinigung zwischen den Tests ausgeführt werden könnte. Der Spülvorgang wird effektiv bis zum letzten Schritt verschoben ... oder zumindest bis zum letzten Schritt, der den ursprünglichen Datensatz erfordert.

Heute werde ich dies erweitern, um weitere Funktionen für REQUIRE_DATA_PURGE hinzuzufügen. Dadurch wird der obige Prozess effektiv invertiert, um sicherzustellen, dass diese Tests erst nach der Datenbereinigung ausgeführt werden, um die Datenwiederherstellung zu testen.

2
kingsisyphus

Ich habe ein Plugin nosedep for Nose implementiert, das Unterstützung für Testabhängigkeiten und Testpriorisierung hinzufügt.

Wie in den anderen Antworten/Kommentaren erwähnt, ist dies oft eine schlechte Idee. Es kann jedoch Ausnahmen geben, bei denen Sie dies tun möchten (in meinem Fall war es Leistung für Integrationstests - mit einem riesigen Aufwand, um in einen testfähigen Zustand zu gelangen, Minuten.) gegen Stunden).

Ein minimales Beispiel ist:

def test_a:
  pass

@depends(before=test_a)
def test_b:
  pass

Um sicherzustellen, dass test_b immer vor test_a ausgeführt wird.

1
Zitrax

Siehe das Beispiel von WidgetTestCase auf http://docs.python.org/library/unittest.html#organizing-test-code , das sagt es

Klasseninstanzen führen jetzt jeweils eine der test _ * () - Methoden aus, wobei self.widget für jede Instanz separat erstellt und gelöscht wird.

Es kann daher nicht sinnvoll sein, die Reihenfolge der Testfälle anzugeben, wenn Sie nicht auf globale Variablen zugreifen.

1
jiakai

Die Philosophie von Unit-Tests besteht darin, sie unabhängig voneinander zu machen. Dies bedeutet, dass der erste Schritt jedes Tests immer darin besteht, zu überdenken, wie Sie jedes Stück entsprechend dieser Philosophie testen. Dies kann das Ändern der Herangehensweise an das Testen und das kreative Arbeiten umfassen, indem die Tests auf kleinere Bereiche beschränkt werden.

Wenn Sie dennoch feststellen, dass Sie Tests in einer bestimmten Reihenfolge benötigen (da dies praktikabel ist), können Sie die Antwort auf Python unittest.TestCase - Ausführungsreihenfolge überprüfen.

0
carrvo

Um die Reihenfolge der Testmethoden zufällig zu bestimmen, können Sie das unittest.TestLoader.sortTestMethodsUsing-Attribut mit einem Affen-Patch versehen

if __== '__main__':
    import random
    unittest.TestLoader.sortTestMethodsUsing = lambda self, a, b: random.choice([1, 0, -1])
    unittest.main()

Der gleiche Ansatz kann verwendet werden, um die Reihenfolge festzulegen, die Sie benötigen.

0
Tomasz Swider