Always use the front door

February 23, 2009 at 7:42 pm 5 comments


Es ist jetzt schon eine ganze Weile her, dass ich mit meine it-agile-Kollegen Sebastian Sanitz und Bernd Schiffer in einem Grails-Projekt zusammengearbeitet habe. In diesem Projekt hatten wir uns entschieden, Oberflächentests mit Canoo Webtest zu schreiben.

Bereits nach kurzer Zeit stießen wir auf Probleme. Wie kann man mit Canoo Webtest direkt auf fachliche Funktionen oder die Datenbank zugreifen, um zu prüfen, dass die Daten korrekt erzeugt wurden?

Bernd vertrat die Ansicht, dass unsere Schwierigkeiten keine Schwäche von Canoo Webtest seien, sondern ein Feature. Es sei keine gute Idee, direkt auf die Fachlogik oder die Datenbank zuzugreifen. Stattdessen solle man die Tests nur gegen die Oberfläche schreiben. Er zitierte dazu ein Prinzip namens “Always use the front door”. Der Zugriff auf Fachlogik oder DB wäre quasi die Hintertür und die Oberläche die Haustür. Sebastian und ich hatten von diesem Prinzip noch nie etwas gehört, aber Bernd hat standhaft behauptet, das sei ein etabliertes Prinzip. (Spätere Internet-Recherchen zeigten, dass man mit dem Suchbegriff in Google nur einen Blogeintrag von Bernd findet.)

Wie auch immer. Wir einigten uns darauf, es mit “Always use the front door” zu versuchen. Das ging eine ganze Weile gut. Dann trat das nächste Problem auf. Wir mussten die Zeit im System nicht nur manipulieren, sondern auch einfrieren, um deterministische Tests zu bekommen. Bei Unittests ist klar, dass man sich dafür einen Zeitgeber baut mit einem passenden Interface. So kann man den Zeitgeber dann mocken und hat damit volle Kontrolle über die Zeit. Aber dieses Vorgehen war ja jetzt verboten. Wir durften nicht einfach eine Funktion im Systen aufrufen, um einen Zeitgeber durch einen Mock zu ersetzen.

Die Lösung für das Problem kam mind. mir etwas eigenartig vor. Wir haben einen TimeController programmiert, so dass man über eine REST-Schnittstelle die Zeit manipulieren und einfrieren kann. Wir haben das System also mit einer Benutzungsschnittstelle versehen, die man fachlich gar nicht braucht. Und natürlich hatten wir den Zusatzaufwand, den Controller so zu entwickeln, dass er im Produktivbetrieb nicht ansprechbar ist – wir wollten ja nicht, dass irgendein Schlauberger die Zeit des Systems manipuliert oder einfriert.

Gut, das funktionierte. Wir konnten den Test formulieren und haben uns an das Haustür-Prinzip gehalten. Aber war das vernünftig? In diesem Fall würde ich die Frage mit “ja” beantworten. Zum einen war der Aufwand für den zusätzlichen Controller in Grails auch nicht größer als hätten wir einen Unittest-artigen Mock programmiert. Und außerdem war der Controller auch beim manuellen Testen des Systems sehr nützlich – klassisch hätte jemand auf dem Server die Systemzeit umgesetzt – und wahrscheinlich vergessen, sie nach dem Test wieder korrekt einzustellen.

Danach kam ein größeres Problem auf uns zu. Unser System verschickt E-Mails. Das kann man mit Canoo Webtest ganz elegant testen. Dummerweise dauert es vom Versand bis zum Empfang einer E-Mails eine undefinierte Zeitspanne – ist halt asynchron. Also haben wir den Test nach Versand der E-Mail warten lassen. Und jedesmal, wenn der Test wegen Timeout fehlschlug, haben wir die Zeitspanne erhöht. Dadurch wurden die Testlaufzeiten immer länger und das grundsätzliche Problem blieb. Wenn die Webtests fehlschlugen, konnten wir auf Anhieb nie sicher sein, ob wir einen Bug hatten oder nur die Zeitspanne für das Warten auf E-Mails zu kurz war. Diese Situation haben wir erstaunlich lange ertragen. Letztlich machte Sebastian dann soviel Druck, dass wir nochmal nachdachten. Wir hatten also eine Situation, die wir bei Unittests niemals akzeptiert hätten. Also haben wir uns gefragt, ob wir die Situation bei den Webtests nicht verbessern können. Die erste Idee war – wieder mal -, das Abrücken vom Haustür-Prinzip. Wenn wir direkt Funktionen des Systems aufrufen würden, könnten wir das Problem Unittest-like lösen: Einfach einen Mock für das E-Mail-System reinklemmen und fertig.

Und tatsächlich funktioniert dieser Ansatz auch dann, wenn man das Haustür-Prinzip befolgen möchte. Dazu ändern wir zunächst unsere Perspekive auf das System: Wir zerteilen unser System in zwei Systeme, das Kernsystem für den eigentlichen Zweck und eines für das E-Mail-Versenden. Das Kernsystem ist vollständig synchron und daher auch automatisiert gut testbar. Das E-Mail-System ist asynchron und schwer automatisiert testbar. Dafür ist es so klein, dass die so entstehende Testlücke risikoarm wird.

Diese Unterteilung kann man ganz unterschiedlich durchführen. Eine Möglichkeit besteht darin, dass das Kernsystem die E-Mails nicht direkt versendet, sondern die E-Mails erstmal in eine Tabelle schreibt. Ein E-Mail-Versende-Job des E-Mail-Systems fragt diese Tabelle regelmäßig ab und versendet die E-Mails. Wurde die E-Mail erfolgreich versendet, wird der entsprechende Datensatz markiert. Für den Test existiert ein spezieller Controller, der den Inhalt dieser E-Mail-Tabelle anzeigt. Der Webtest arbeitet mit dieser Tabelle und wird daher wieder deterministisch und schnell. Und auch in diesem Fall bringt uns diese Herangehensweise einen positiven Seiteneffekt. Man kann den Controller zum Monitoring der Anwendung verwenden und so im Produktivbetrieb besser kontrollieren, was im System passiert.

Eine andere (von Sebastian vorgeschlagene) Variante wäre, Kernsystem und E-Mail-System nicht über die DB zu koppeln, sondern z.B. über REST-Aufrufe. Dann könnte man für den Test ein Dummy-E-Mail-System konfigurieren, dass die REST-Aufrufe entgegennimmt und protokolliert und der Test würde dieses Protokoll prüfen. Und dann könnte man sogar soweit gehen, dass man hier einen generischen Protokoll-Service einsetzt, der einfach alle REST-Aufrufe protokolliert und (wie Unittest-Mocks) vordefinierte Antworten liefert. Das hört sich echt cool an. Das sollte man jemand bauen. Sebastian?

Insgesamt kann man also sagen: Wir wissen nicht wirklich, woher das Prinzip stammt und ob Bernd es sich selbst ausgedacht hat, aber es funktioniert auch für Akzeptanztests sehr gut.

Entry filed under: #. Tags: .

Umbau meiner Website, Schritt 1 Notebooks: IBM/Lenovo vs. DELL

5 Comments Add your own

  • 1. Johannes  |  February 24, 2009 at 8:30 pm

    Für mich ist eine “Vordertür”, die man nicht im System braucht, dasselbe wie eine Hintertür, also lediglich eine Verschleierung der Tatsache, dass ein Testclient, der das Web benutzt auch das Web für seine Hintertür benutzen muss.

    Ich gebe dir recht, dass es oft Sinn ergibt, eine anfängliche Hintertür zu einer Vordertür zu machen, aber nicht immer. Beispiel: Das Online-Banking-System, dem ich eine Hintertür spendiere, um den Saldo eines Kontos beliebig zu manipulieren. Ist selbst für das Inhouse-System der Bank eine wirklich schlechte Idee. Manchmal ist auch der Weg durch die Vordertür einfach zu aufwändig und die Testlaufzeiten erfordern eine Hintertür. Und sehr oft heißt die Hintertürfunktion
    einfach: Reset Everything; was auch meist keine gute Vordertür ergibt.

  • 2. Jens Himmelreich  |  February 24, 2009 at 11:04 pm

    “xUnit Test Pattern” kennt “Front Door First”

  • 3. stefanroock  |  February 25, 2009 at 8:15 am

    Danke für den Link, Jens.

    So ist das Prinzip nicht so rigide formuliert, wie die Version von Bernd. Und dann passt es auch besser zu Johannes’ Anmerkungen.

  • 4. marko  |  February 25, 2009 at 1:42 pm

    Link? Ich habe keinen Link in Jens’ Beitrag gesehen. Hat WordPress den geschluckt? Mit dem Hinweis auf das Buch habe ich aber http://xunitpatterns.com/Principles%20of%20Test%20Automation.html#Use%20the%20Front%20Door%20First und http://xunitpatterns.com/Back%20Door%20Manipulation.html gefunden.

  • 5. stefanroock  |  February 25, 2009 at 3:12 pm

    Ich meinte “link” metaphorisch. Mit dem Jens’ Hinweis findet man mit Google die entsprechenden Internetseiten sehr einfach.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Trackback this post  |  Subscribe to the comments via RSS Feed



%d bloggers like this: