Gibt es in Python eine Möglichkeit unittest
, die Reihenfolge festzulegen , in der Testfälle ausgeführt werden?
In meiner aktuellen TestCase
Klasse haben einige Testfälle Nebenwirkungen, die Bedingungen für den ordnungsgemäßen Betrieb der anderen festlegen. Jetzt ist mir klar, dass der richtige Weg, dies setUp()
zu tun, darin besteht, alle Setup-Aufgaben zu erledigen, aber ich möchte ein Design implementieren, bei dem jeder aufeinanderfolgende Test etwas mehr Status erzeugt, als der nächste verwenden kann. Ich finde das viel eleganter.
class MyTest(TestCase):
def test_setup(self):
#do something
def test_thing(self)
#do something that depends on test_setup()
Idealerweise möchte ich, dass die Tests in der Reihenfolge ausgeführt werden, in der sie in der Klasse erscheinen. Es scheint, dass sie in alphabetischer Reihenfolge ausgeführt werden.
quelle
sorted()
nicht unbedingt erforderlich ist, dadir()
die Schrittmethoden alphabetisch nach Garantie sortiert zurückgegeben werden. Aus diesem Grund werdenunittest
die Testklassen und Testmethoden standardmäßig auch in alphabetischer Reihenfolge behandelt (auch wenn dies nichtsortTestMethodsUsing
der Fall ist). Dies kann aus praktischen Gründen genutzt werden, um beispielsweise die neuesten Arbeitstests zuerst ausführen zu lassen, um den Edit-Testrun-Zyklus zu beschleunigen.Es ist eine gute Praxis, immer einen monolithischen Test für solche Erwartungen zu schreiben. Wenn Sie jedoch ein doofer Typ wie ich sind, können Sie einfach hässlich aussehende Methoden in alphabetischer Reihenfolge schreiben, damit sie von a nach b sortiert werden, wie in den Python-Dokumenten http erwähnt : //docs.python.org/library/unittest.html
BEISPIEL:
def test_a_first(): print "1" def test_b_next(): print "2" def test_c_last(): print "3"
quelle
http://docs.python.org/library/unittest.html
Stellen Sie also sicher, dass
test_setup
der Name den kleinsten Zeichenfolgenwert hat.Beachten Sie, dass Sie sich nicht auf dieses Verhalten verlassen sollten - verschiedene Testfunktionen sollten unabhängig von der Ausführungsreihenfolge sein. In der obigen Antwort von ngcohlan finden Sie eine Lösung, wenn Sie ausdrücklich eine Bestellung benötigen.
quelle
Alte Frage, aber eine andere Art, die ich in verwandten Fragen nicht gesehen habe: Verwenden Sie a
TestSuite
.Eine andere Möglichkeit, die Bestellung abzuschließen, besteht darin, die Tests zu a hinzuzufügen
unitest.TestSuite
. Dies scheint die Reihenfolge zu berücksichtigen, in der die Tests mithilfe von zur Suite hinzugefügt werdensuite.addTest(...)
. Um dies zu tun:Erstellen Sie eine oder mehrere TestCase-Unterklassen.
class FooTestCase(unittest.TestCase): def test_ten(): print('Testing ten (10)...') def test_eleven(): print('Testing eleven (11)...') class BarTestCase(unittest.TestCase): def test_twelve(): print('Testing twelve (12)...') def test_nine(): print('Testing nine (09)...')
Erstellen Sie eine aufrufbare Testsuite-Generation, die in der gewünschten Reihenfolge hinzugefügt wurde und aus den Dokumenten und dieser Frage angepasst wurde :
def suite(): suite = unittest.TestSuite() suite.addTest(BarTestCase('test_nine')) suite.addTest(FooTestCase('test_ten')) suite.addTest(FooTestCase('test_eleven')) suite.addTest(BarTestCase('test_twelve')) return suite
Führen Sie die Testsuite aus, z.
if __name__ == '__main__': runner = unittest.TextTestRunner(failfast=True) runner.run(suite())
Für den Kontext hatte ich dies gebraucht und war mit den anderen Optionen nicht zufrieden. Ich habe mich für die oben beschriebene Art der Testbestellung entschieden. Ich habe nicht gesehen, dass diese TestSuite-Methode eine der verschiedenen "Fragen zur Bestellung von Komponententests" auflistet (z. B. diese und andere Fragen, einschließlich Ausführungsreihenfolge oder Änderungsreihenfolge oder Testreihenfolge ).
quelle
@classmethod
, können sie den Status über Instanzen hinweg beibehalten .setUpClass
aufgerufen wird? Oder muss es manuell ausgeführt werden?@classmethod != @staticmethod
!!! Sei vorsichtig, es sind ganz andere Dinge.@staticmethod
Mit dieser Option können Sie die Methode aufrufen, ohne über eine Instanz der Klasse zu verfügen.@classmethod
gibt Ihnen Zugriff auf die Klasse und in der Klasse selbst können Sie Informationen speichern. Wenn Sie dies beispielsweisecls.somevar = 10
innerhalb einer Klassenmethode tun, sehen alle Instanzen dieser Klasse und alle anderen Klassenmethoden dies,somevar = 10
nachdem diese Funktion ausgeführt wurde. Klassen selbst sind Objekte, an die Sie Werte binden können.Am Ende hatte ich eine einfache Lösung, die für mich funktionierte:
class SequentialTestLoader(unittest.TestLoader): def getTestCaseNames(self, testCaseClass): test_names = super().getTestCaseNames(testCaseClass) testcase_methods = list(testCaseClass.__dict__.keys()) test_names.sort(key=testcase_methods.index) return test_names
Und dann
quelle
Tests, die wirklich voneinander abhängen, sollten explizit zu einem Test verkettet werden.
Bei Tests, für die unterschiedliche Setup-Ebenen erforderlich sind, kann auch das entsprechende
setUp()
Setup ausreichend ausgeführt werden - auf verschiedene Arten denkbar.Andernfalls werden
unittest
die Testklassen und Testmethoden innerhalb der Testklassen standardmäßig in alphabetischer Reihenfolge behandelt (auch wenn "loader.sortTestMethodsUsing
Keine" ist).dir()
wird intern verwendet, was nach Garantie sortiert.Das letztere Verhalten kann aus Gründen der Praktikabilität ausgenutzt werden - z. B. um die neuesten Arbeitstests zuerst ausführen zu lassen, um den Edit-Testrun-Zyklus zu beschleunigen. Dieses Verhalten sollte jedoch nicht verwendet werden, um echte Abhängigkeiten herzustellen . Beachten Sie, dass Tests einzeln über Befehlszeilenoptionen usw. ausgeführt werden können.
quelle
@ ncoghlans Antwort war genau das, wonach ich gesucht habe, als ich zu diesem Thread kam. Am Ende habe ich es so geändert, dass jeder Stufentest ausgeführt werden kann, selbst wenn ein vorheriger Schritt bereits einen Fehler ausgelöst hat. Dies hilft mir (und vielleicht Ihnen!), die Ausbreitung von Fehlern in datenbankzentrierter Multithread-Software zu entdecken und zu planen.
class Monolithic(TestCase): def step1_testName1(self): ... def step2_testName2(self): ... def steps(self): ''' Generates the step methods from their parent object ''' for name in sorted(dir(self)): if name.startswith('step'): yield name, getattr(self, name) def test_steps(self): ''' Run the individual steps associated with this test ''' # Create a flag that determines whether to raise an error at # the end of the test failed = False # An empty string that the will accumulate error messages for # each failing step fail_message = '' for name, step in self.steps(): try: step() except Exception as e: # A step has failed, the test should continue through # the remaining steps, but eventually fail failed = True # get the name of the method -- so the fail message is # nicer to read :) name = name.split('_')[1] # append this step's exception to the fail message fail_message += "\n\nFAIL: {}\n {} failed ({}: {})".format(name, step, type(e), e) # check if any of the steps failed if failed is True: # fail the test with the accumulated exception message self.fail(fail_message)
quelle
Eine einfache und flexible Möglichkeit besteht darin, eine Komparatorfunktion zuzuweisen
unittest.TestLoader.sortTestMethodsUsing
:Minimale Nutzung:
import unittest class Test(unittest.TestCase): def test_foo(self): """ test foo """ self.assertEqual(1, 1) def test_bar(self): """ test bar """ self.assertEqual(1, 1) if __name__ == "__main__": test_order = ["test_foo", "test_bar"] # could be sys.argv loader = unittest.TestLoader() loader.sortTestMethodsUsing = lambda x, y: test_order.index(x) - test_order.index(y) unittest.main(testLoader=loader, verbosity=2)
Ausgabe:
Hier ist ein Proof-of-Concept zum Ausführen von Tests in der Quellcode-Reihenfolge anstelle der lexikalischen Standardreihenfolge (Ausgabe wie oben).
import inspect import unittest class Test(unittest.TestCase): def test_foo(self): """ test foo """ self.assertEqual(1, 1) def test_bar(self): """ test bar """ self.assertEqual(1, 1) if __name__ == "__main__": test_src = inspect.getsource(Test) unittest.TestLoader.sortTestMethodsUsing = lambda _, x, y: ( test_src.index(f"def {x}") - test_src.index(f"def {y}") ) unittest.main(verbosity=2)
Ich habe Python 3.8.0 in diesem Beitrag verwendet.
quelle
Ein Ansatz kann darin bestehen, diese Untertests nicht als Tests nach
unittest
Modulen zu behandeln, indem Sie sie anhängen_
und dann einen Testfall erstellen, der auf der richtigen Reihenfolge dieser ausgeführten Unteroperationen aufbaut.Dies ist besser, als sich auf die Sortierreihenfolge des
unittest
Moduls zu verlassen, da sich dies morgen ändern könnte und auch das Erreichen einer topologischen Sortierung in der Reihenfolge nicht sehr einfach ist.Ein Beispiel für diesen Ansatz, genommen von hier (Disclaimer: mein eigenes Modul) , ist als unten.
Hier führt der Testfall unabhängige Tests durch, z. B. die Überprüfung auf Tabellenparameter, die nicht gesetzt sind (
test_table_not_set
) oder auf Primärschlüssel (test_primary_key
), die noch parallel sind. Der CRUD-Test ist jedoch nur dann sinnvoll, wenn er in der richtigen Reihenfolge und im richtigen Status durchgeführt wurde, die durch vorherige Operationen festgelegt wurden. Daher wurden diese Tests eher nur getrennt durchgeführt,unit
aber nicht getestet. Ein anderer Test (test_CRUD
) erstellt dann eine richtige Reihenfolge dieser Operationen und testet sie.import os import sqlite3 import unittest from sql30 import db DB_NAME = 'review.db' class Reviews(db.Model): TABLE = 'reviews' PKEY = 'rid' DB_SCHEMA = { 'db_name': DB_NAME, 'tables': [ { 'name': TABLE, 'fields': { 'rid': 'uuid', 'header': 'text', 'rating': 'int', 'desc': 'text' }, 'primary_key': PKEY }] } VALIDATE_BEFORE_WRITE = True class ReviewTest(unittest.TestCase): def setUp(self): if os.path.exists(DB_NAME): os.remove(DB_NAME) def test_table_not_set(self): """ Tests for raise of assertion when table is not set. """ db = Reviews() try: db.read() except Exception as err: self.assertIn('No table set for operation', str(err)) def test_primary_key(self): """ Ensures , primary key is honored. """ db = Reviews() db.table = 'reviews' db.write(rid=10, rating=5) try: db.write(rid=10, rating=4) except sqlite3.IntegrityError as err: self.assertIn('UNIQUE constraint failed', str(err)) def _test_CREATE(self): db = Reviews() db.table = 'reviews' # backward compatibility for 'write' API db.write(tbl='reviews', rid=1, header='good thing', rating=5) # New API with 'create' db.create(tbl='reviews', rid=2, header='good thing', rating=5) # backward compatibility for 'write' API, without tbl, # explicitly passed db.write(tbl='reviews', rid=3, header='good thing', rating=5) # New API with 'create', without table name explicitly passed. db.create(tbl='reviews', rid=4, header='good thing', rating=5) db.commit() # save the work. def _test_READ(self): db = Reviews() db.table = 'reviews' rec1 = db.read(tbl='reviews', rid=1, header='good thing', rating=5) rec2 = db.read(rid=1, header='good thing') rec3 = db.read(rid=1) self.assertEqual(rec1, rec2) self.assertEqual(rec2, rec3) recs = db.read() # read all self.assertEqual(len(recs), 4) def _test_UPDATE(self): db = Reviews() db.table = 'reviews' where = {'rid': 2} db.update(condition=where, header='average item', rating=2) db.commit() rec = db.read(rid=2)[0] self.assertIn('average item', rec) def _test_DELETE(self): db = Reviews() db.table = 'reviews' db.delete(rid=2) db.commit() self.assertFalse(db.read(rid=2)) def test_CRUD(self): self._test_CREATE() self._test_READ() self._test_UPDATE() self._test_DELETE() def tearDown(self): os.remove(DB_NAME)
quelle