Sonntag, Juli 27, 2008

Google-Testing-Blog: Testing Against Interfaces

Auf dem Google-Testing-Blog ist gerade ein Artikel "Testing Against Interfaces" erschienen. Es geht darum, wie man verschiedene Implementation desselben Interfaces testet. Der Artikel schlägt vor, eine abstrakte Testklasse zu dem Interface zu bauen. Im setUp dieser abstrakten Testklasse wird eine abstrakte Methode createXYZ aufgerufen, die das zu testende Objekt liefert. Jetzt leitet man von der abstrakten Testklasse konkrete Testklasse je getesteter Klasse ab. In diesem konkreten Testklassen überschreibt man die createXYZ-Methode und kann dann verkünden: "ich habe fertig" :-)

Das klingt plausibel, ist nach meiner Erfahrung in vielen Fällen aber nicht praktikabel. Dafür sind zwei Gründe verantwortlich:
  1. Man benötigt in Tests das getestete Objekt mitunter in unterschiedlichen Ausprägungen, und muss mitunter unterschiedliche Konstruktoren aufrufen. Dann muss man die createXYZ-Methode parametrisieren. Das funktioniert aber nur solange gut, wie alle Implementationen des getesteten Interfaces dieselben Parameter bekommen. Ist das nicht der Fall, muss man die Signatur der createXYZ-Methode aufblähen mit Parametern, die nur für einzelne Implementationen einen Sinn ergeben. Hinweis: Das Kochbuch zu JUnit benutzt z.B. unterschiedlich erzeugte Objekte im selben Test (natürlich ohne die Interface-Problematik).
  2. Häufig reicht ein einzelnes Objekt als Fixture nicht aus. Es werden mehrere Objekte benötigt. Jetzt müsste die createXYZ-Methode mehrere Objekte zurück liefern (als Liste?) oder man ruft mehrere createXYZ-Methoden auf, oder... Alles nicht besonders elegant.
Diese Probleme waren bei uns in früheren Jahren in der Praxis so massiv, dass wir diese Konstruktion fallen gelassen haben. Stattdessen haben wir eine andere Lösung konzipiert: Wir bauen ein Test-Center auf. Das ist eine konkrete Klasse, die mir einzelne parametrisierte Testmethoden anbietet. Diese ruft man aus den konkreten Testklassen auf. Damit eliminiert man die Redundanzen in den Tests. Natürlich sind meine Tests etwas länger, weil man die Testmethoden zur Delegation trotzdem alle hinschreiben muss, a la


public void testXYZ() { meinTestCenter.testXYZ(meinObjekt); }


Als weiteren Vorteil bringt die Test-Center-Lösung die Abwesenheit von Vererbung. Die Kopplung zwischen den Testklassen wird also reduziert.

Interessanterweise hatten wir für solche Konstruktionen umso weniger Anwendungsfälle, desto mehr wir uns von technischen Frameworks hin zu konkreten Anwendungssystemen orientiert haben.

Anmerkung: Das Fixtures aus mehreren Objekten bestehen, bedeutet noch nicht automatisch, dass es sich um Integrationstests handelt. Es ist meiner Meinung nach erlaubt und auch notwendig, in Unit-Tests wenige eng zusammenarbeitende Objekte als eine Fixture zu begreifen. Ansonsten neigen die Tests zur Trivialität und in statisch getypten Sprachen wie Java müsste ich überall Interfaces einziehen und in jedem Test Mocks benutzen.

Post bewerten

Keine Kommentare: