Mocks, Stubs, Fakes, Dummies und Test Doubles – Anwendungsentwickler-Podcast #143

IT-Berufe-Podcast - A podcast by Stefan Macke - Mondays

Um Möglichkeiten, Abhängigkeiten in Tests loszuwerden, geht es in der einhundertdreiundvierzigsten Episode des Anwendungsentwickler-Podcasts. Inhalt Vorweg: Automatisierte Tests gibt es nicht nur für objektorientierte Software, sondern natürlich auch für funktionale, prozedurale usw. Die folgenden Inhalte beziehen sich aber ausschließlich auf die Objektorientierung. In anderen Paradigmen haben die genannten Begriffe evtl. andere Bedeutungen oder die vorgestellten Lösungen funktionieren etwas anders, da es z.B. keine Polymorphie gibt. Grundlagen * Automatisierte Tests sollen das Verhalten unseres Systems prüfen und nur fehlschlagen, wenn ein Fehler im Code vorliegt. Sie sollen schnell und wiederholbar sein, damit sie so oft wie möglich ausgeführt werden. Sie sollen immer und überall (auf allen Rechnern/Umgebungen) ausführbar sein. * Unit-Tests prüfen das Verhalten einer einzelnen Komponente, z.B. eine Methode, in Isolation. * Integrationstests prüfen das Verhalten mehrerer Komponenten, z.B. Objekte, im Zusammenspiel. * Integrationstests werden auch Tests genannt, die die Infrastruktur berühren, also z.B. eine Datenbank, das Netzwerk oder das Dateisystem. * Die Infrastruktur sollte in Tests nicht berührt werden, da diese schnell Fehler produziert: erwartete Datenbankinhalte können sich ändern, im Dateisystem fehlen Berechtigungen oder das Netzwerk ist nicht verfügbar. * Die Isolation von Komponenten ist schon in kleinen Systemen nicht immer einfach. Ein Objekt kann seine Aufgaben fast nie komplett allein erledigen, sondern braucht andere Objekte dafür. Ein Service braucht vielleicht ein Repository, um die zu verarbeitenden Daten aus der Datenquelle zu lesen. Ist kein Repository vorhanden, gibt es vielleicht eine NullPointerException beim Aufruf der zu testenden Methode. * Diese Abhängigkeiten machen die Tests schwierig, da das zu testende Objekt nicht korrekt funktioniert, wenn sie nicht vorhanden sind. Somit müssen alle für den konkreten Test benötigten Abhängigkeiten durch diesen bereitgestellt werden. * Somit enthält ein Test nicht nur die eigentlich zu testende Komponente, sondern auch noch ihre Abhängigkeiten. Damit klar ist, welche der verschiedenen Komponenten nun eigentlich getestet werden soll, bekommt sie die Bezeichnung System under test (abgekürzt SUT). * Beim Test können grundsätzlich die „echten“ Komponenten verwendet werden, falls dies möglich und sinnvoll ist. Oder die Komponenten werden durch sog. Test Doubles ersetzt, wie ein Stuntdouble den eigentlichen Schauspieler ersetzt. Test Doubles * Test Doubles sind der Oberbegriff für Komponenten, die in Tests verwendet werden, um die Abhängigkeiten des SUT zu ersetzen. Sie sollen vor allem für vorhersagbare Testergebnisse sorgen, indem z.B. immer die gleichen Werte aus dem Speicher zurückgegeben werden und nicht potentiell unterschiedliche Werte aus der Datenbank gelesen werden. * Damit das Ganze funktioniert, müssen die echten Komponenten durch die Test Doubles ersetzt werden können. In objektorientierter Software kommt dabei die Polymorphie zum Einsatz. Die Abhängigkeiten müssen also z.B. als Interface oder als (abstrakte) Basisklasse vorliegen, damit die Test Doubles anstelle der echten Komponenten genutzt werden können. * Außerdem ist es nötig, dass die Test Doubles dem SUT „untergejubelt“ werden können. Es ist also irgendeine Form von Dependency Injection nötig, z.B. Konstruktorparameter oder Setter-Methoden. Sobald das SUT sich selbst seine Abhängigkeiten erzeugt (z.B. mit new), ist ein Test mit Test Doubles schwierig oder gar unmöglich. * Das alles hat auch eine Auswirkung auf den Produktivcode. Denn wenn das SUT eine Abhängigkeit als Konstruktorparameter übergeben bekommen muss, wird auch der Produktivcode die echte Komponente so hineingeben müssen. * Die Tests haben somit indirekt zur Folge,

Visit the podcast's native language site