Hallo zusammen,
ich brauche mal wieder Anregungen bezüglich Unittests. Und zwar habe ich ein Package von sich untereinander importierenden Modulen. Für jedes Modul existiert ein Testmodul in der ich die TestCases definiere. Dann gibt es noch ein Modul 'runtests', in dem ich alle Testmodule importiere und in eine Suite einfüge, die dann ausgeführt wird.
Das hat bislang ganz gut funktioniert. Mit dem erstgenannten Package bin ich aber in Probleme gerannt, weil ich in den einzelnen Testmodulen Namen manipuliere um Funktionalitäten möglichst unabhängig von anderen Modulen testen zu können. Ein Beispiel: Der Testkandidat importiert ein Modul 'av3', welches z.B. Schnittstellen zur Programmkonfiguration ('config') und zur abgefragten Datenbank ('db') enthält. Um jetzt einfacher testen zu können, ersetze ich testkandidat.av3.config durch ein Mockobjekt. Wenn ich jetzt nicht höllisch aufpasse und diesen 'Patch' nach Ende der Tests (in diesem Modul) rückgängig mache, beeinflußt mir diese Änderung unter Umständen eines der späteren Testmodule. Und nicht immer ist es mit einem einfachen reload(av3) getan.
Was mich jetzt wiederum zum Nachdenken angeregt hat: Ist es wirklich so sinnvoll, importierte Module in der oben beschriebenen Weise zu manipulieren? Vielleicht ist mein Denkansatz ja völlig falsch? Ich finde im Netz leider wenig zu Best practices. Wie geht ihr in solchen Fällen vor?
Schönen Dank schon mal
Edit: Titel und Rechtschreibung
Unittests (Best practices)
Nein. Du sagst ja selber, dass Du "höllisch" aufpassen mußt, um da noch Übersicht zu halten. Dann findest Du ja schon, dass es nicht optimal ist. Also gibt es einen sinnvolleren Weg. (Tolle Logik, oder? )Pekh hat geschrieben:Was mich jetzt wiederum zum Nachdenken angeregt hat: Ist es wirklich so sinnvoll, importierte Module in der oben beschriebenen Weise zu manipulieren?
Völlig falsch. Na ja, das vielleicht nicht ...Pekh hat geschrieben:Vielleicht ist mein Denkansatz ja völlig falsch?
Ganz ähnlich. Deine Beschreibung des Testsuiteaufbaus finde ich gar nicht mal so schlecht - eigentlich völlig normal. Nur das mit dem Mockobjekt verstehe ich nicht: Ist das wirklich notwendig? Wirklich? Reicht die Trennung der Namespaces durch die versch. Testmodule nicht aus? Dann ist es vielleicht Zeit zum Refactoring. Oder sonst lass doch mal ein solches Testmodul sehen: Vielleicht kommt uns eine gute Idee, wo das Problem zu suchen ist.Pekh hat geschrieben:Wie geht ihr in solchen Fällen vor?
HTH
Christian
Das mit der Notwendigkeit ist die Frage, die mich seit Tagen beschäftigt. Aber ich drehe mich irgendwie immer im Kreis und komme irgendwie zu keiner wirklich besseren Lösung.CM hat geschrieben: Nur das mit dem Mockobjekt verstehe ich nicht: Ist das wirklich notwendig? Wirklich? Reicht die Trennung der Namespaces durch die versch. Testmodule nicht aus?
Ganz typisch sind bei mir im Moment solche Konstrukte, natürlich mehr oder weniger angepaßt:
Code: Alles auswählen
import unittest
import mock
from sqlalchemy.exceptions import OperationalError
import av3
class TestAV3(unittest.TestCase):
def setUp(self):
cd = mock.Mock()
cd.get_var.return_value = u"sqlite:///tests/testdaten/av3_test.sqlite3"
av3.config = cd
def tearDown(self):
reload(av3.alchemist)
reload(av3)
Noch mal zur Verdeutlichung:
av3 importiert alchemist und plugins (als Package organisiert)
die Plugins sind wiederum Module bzw. Packages mit definierten Eigenschaften und können z.B. Klassen für polymorphe Mappings bereitstellen. Dazu müssen sie ebenfalls den Alchemisten importieren (weil da die Ur-Klasse drinsteckt)
Wenn ich jetzt den Alchemisten neu laden, weil ich im Test was manipuliert habe, dann breche ich mir die Subclass-Beziehung zwischen Plugin und Ur-Klasse.
Insofern: Es ist was faul in meinem Staat. Wahrscheinlich sogar mehrere Dinge. Spontan fallen mir da ein: Organisation der Module, Ungünstiges Plugin-Konzept, falsche Herangehensweise an die Unittests.
Wahrscheinlich könnte man das Thema auch auf allgemeine Organisations-Praktiken erweitern, ich möchte mich aber trotzdem gerne auf die Organisation der Unittests konzentrieren.
Hm. Oder vielleicht auch nicht
Moin,
du kannst auch Mock-Objekte gezielt in Funktionen/Methoden injizieren. Wenn du eine Funktion/Methode testen willst und diese ein bestimmtes, bereits importiertes Modul benutzt, dann könntest du folgendes machen:
Musst halt dran denken, die Mock-Objekte wieder aus den func_globals zu entfernen. Der Vorteil ist, dass nicht gleich ganze Module verwurstelt werden.
Ich mach das so und es funktioniert richtig gut. Mit Kontext-Managern könnte man das vielleicht sogar eleganter hinbekommen. Ich selbst bin aber noch auf Python2.4 festgenagelt .
Gruß,
Manuel
du kannst auch Mock-Objekte gezielt in Funktionen/Methoden injizieren. Wenn du eine Funktion/Methode testen willst und diese ein bestimmtes, bereits importiertes Modul benutzt, dann könntest du folgendes machen:
Code: Alles auswählen
import mymodule
import mock
class MyModuleTest(unittest.TestCase):
def test_function(self):
mymodule.my_function.func_globals['to_be_mocked_module'] = mock.Mock()
try:
self.assertWhatEver(mymodule.my_function(), 'whatever')
finally:
del mymodule.my_function.func_globals['to_be_mocked_module']
Ich mach das so und es funktioniert richtig gut. Mit Kontext-Managern könnte man das vielleicht sogar eleganter hinbekommen. Ich selbst bin aber noch auf Python2.4 festgenagelt .
Gruß,
Manuel
Ja, das sieht wirklich gut aus. Werde trotzdem noch mal durch den Code gehen, und die Funktionen / Methoden so anpassen, daß sie nach Möglichkeit auch separat getestet werden können. Bin zwar eigentlich davon ausgegangen, daß das schon der Fall wäre (seit ich unittest verwende, hat sich mein Entwurfsstil schon stark verändert), aber es fallen mir doch immer noch Stellen auf, die man (in dieser Hinsicht) besser lösen könnte.
Trotzdem gehe ich davon aus, daß einige Stellen bleiben werden, an die ich anders nicht herankomme. Und da ist die von dir vorgeschlagene Methode sicherlich besser, als meine bisherige.
Schönen Dank euch beiden!
Trotzdem gehe ich davon aus, daß einige Stellen bleiben werden, an die ich anders nicht herankomme. Und da ist die von dir vorgeschlagene Methode sicherlich besser, als meine bisherige.
Schönen Dank euch beiden!