T-Rex: was wird alles per Unittest getestet?

Moin,

..."einfach alles!" wird man vermutlich sagen. Mein kongreter Fall. Ich habe eine Seite die per ID einen Eintrag aus der Datenbank anzeigt. Das ganze läuft über eine Template Engine. Ich frage mich jetzt ob ich testen sollte ob die Variablen in der Template Engine richtig gesetzt sind. Das würde nämlich eine Menge arbeit bedeuten + hoher Aufwandt die Tests zu warten.

Auf der anderen Seite, solange keine größere Logik dabei ist die etwas berechnet, würde es doch reichen hier auf der Webseite selbst die Dinge zu testen. Also wirklich Browser auf und gucken ob alles richtig angezeigt wird.

Wenn ich nach Artikeln bezüglich Unittests suche finde ich leider nur wie man einen Unittests schreibt (also Technisch) aber nicht was man testen sollte :(.

Gruß
schönes Wochenende wünschender
T-Rex

  1. Hallo,
    Prinzipiell solltest Du (idealerweise ;) ), alles testen, was eine einzelne, gekappselte Einheit (daher "Unit-Test") in Deiner Software beschreibt.
    Es ist in der Regel problematisch, einen Unit-Test zu schreiben, der mehr als eine Komponente beinhaltet. Erstens ist es dann eigentlich kein "Unit-Test" mehr, zweitens kann man den kompletten Test wegwerfen, sobald man nur eine einzelne Komponente austauscht, drittens lässt sich dann ein eventueller Fehler um so schwerer finden, weil Du mehrere Komponenten untersuchen musst.

    In Deinem konkreten Fall:
    Ich stelle mir da jetzt mal so vor, Du hast in Deiner Anwendung 4 Komponenten (vielleicht siehts bei Dir auch etwas anders aus)

    1.) Datenbank-System
    2.) Persistenzschicht (ein OR-Mapper, oder ein paar Klassen, die die Inhalte der Datenbank auslesen/verändern und daraus Objekte bauen) (siehe Modell im MVC-Modell)
    3.) Business-Logik (Controller im MVC)
    4.) Template-Engine (View im MVC)

    Streng genommen müsstest Du nun diese 4 Komponenten jede für sich Unit-testen. Bei der Datenbank kannst Du aber vermutlich gutgläubig davon ausgehen, dass sie funktioniert :), also bleiben noch die anderen drei

    Also bräuchtest Du mindestens 3 Unit-Tests.

    Der Witz dabei ist nun, dass Du die jeweils anderen Komponenten durch Dummy-Objekte (Mocks) ersetzt, die verlässlich immer genau das liefern was Du brauchst. Somit stellst Du sicher, dass ein Test für eine Komponente nur dann fehlschlägt, wenn es wirklich an dieser Komponente liegt.

    Wenn Du also z.b. die Template-Engine testen willst, baust Du einen "ControllerMock", der genau das liefert, was Du gerade testen willst, und überprüfst dann, ob die Template-Engine auch richtig darauf reagiert.
    Wenn Du den Controller testen willst, baust Du einen "PersistenceMock" welcher anstelle der echten Datenbank-Schicht Objekte ausspuckt, und stellst dann im Test sicher, dass der Controller diese Daten so aufbereitet, wie es die Template-Engine erwartet.

    Auf der anderen Seite, solange keine größere Logik dabei ist die etwas >berechnet, würde es doch reichen hier auf der Webseite selbst die Dinge zu >testen. Also wirklich Browser auf und gucken ob alles richtig angezeigt >wird.

    Dies mag funktionieren, wenn Deine Anwendung klein und überschaubar ist - in so einem Fall rentiert sich auch manchmal der Aufwand für Unit-Tests nicht.
    Wenn Du aber z.b. an einer großen Webanwendung schraubst, an der täglich dutzende Entwickler herumpfuschen, möchtest Du das nicht mehr manuell tun, sondern einen automatisierten Prozess, der Alarm schlägt, sobald irgend etwas nicht mehr funktioniert (siehe Continous Integration). Das geht nur über Unit-Tests oder vergleichbare Methoden.

    1. Danke für deine Ausführliche Antwort.

      Genau wie du es beschrieben hast hab ich das aufgebaut. Es gibt in der Tat diese 4 Schichten bei mir. Wobei es zwischen 3 und 4 noch eine Schicht 3.5 gibt, welche die Daten die der Controller liefert in die Template-Engine setzt. Diese Schicht ist für das richtige Formatierung der Daten zuständig.

      Als Beispiel:
      Es gibt ein User Objekt, dass alle Daten zum User enthalt unter anderem das Anmeldedatum. Dieses Objekt wird an schicht 3.5 Übergeben. Diese holt aus dem Userobjekt alle Daten und schmeisst sie in die Template Engine und definiert dabei Namen für die Variablen. Das Anmeldedatum formt diese Schicht dann von einem timestamp in ein "lesbares" Format um. Wird irgendwann die Mehrsprachigkeit eingestellt kann dieses lesbares format auch eine Englische, holländische Datumsanzeige sein.

      Ich frage mich ob ich diese Schicht auch testen soll. Der Test würde dann in etwas so aussehen:
      Bei User Anmeldedatum setzten
      User in Schicht 3.5
      bei TemplateEnginge gucken ob Anmeldedatum richtig erzeugt wurde

      Kommt jetzt ein weiteres Feld zum User müsste dieses auch wieder getestet werden und so weiter...
      Das sieht für mich nach einer enormen Wartungsvollen Sache aus. Deswegen hätte ich diesen Part manuell vorgenommen?

      Achja, bis auf Schicht 3.5 steht alles auf Unittests. Wobei ich mir auch die Frage stelle ob ich total simple Controller auch auf Unittests stellen soll. Die sehen ungefähr so aus:
      1. Lade Datensatz anhand einer Id
      2. setzte Datensatz ins Template
      Auch hier würde man sofort auf der Webseite sehen wenn etwas nicht richtig läuft.

      Gruß
      praktisch denkender
      T-Rex

      1. Hallo,

        Ich frage mich ob ich diese Schicht auch testen soll. Der Test würde dann in etwas so aussehen:
        Bei User Anmeldedatum setzten
        User in Schicht 3.5
        bei TemplateEnginge gucken ob Anmeldedatum richtig erzeugt wurde

        Genau.
        Dann würdest Du also einen UserMock bauen, der nichts können muss, ausser, dass man von ihm einen Timestamp auslesen kann. Dein Test würde dann den diesen User(-mock) nehmen, ihn in die Schicht 3.5 stecken und prüfen, ob das herauskommende Datum so ist, wie Du es haben willst (also lesbar).

        Kommt jetzt ein weiteres Feld zum User müsste dieses auch wieder getestet werden und so weiter...
        Das sieht für mich nach einer enormen Wartungsvollen Sache aus. Deswegen hätte ich diesen Part manuell vorgenommen?

        In der Tat, primitive Operationen würde ich auch nicht immer unit-testen. Wenn ein "User.setAnmeldedatum(...)" funktioniert, wird vermutlich auch ein "User.setVorname(...)" und ein "User.setPasswort(...)" funktionieren.
        Interessant sind Unit-Tests immer dann, wenn komplexe Logik dahinter steht, wo man leicht etwas kaputt machen kann, wenn man etwas ändert/erweitert.

        Wobei ich mir auch die Frage stelle ob ich total simple Controller auch auf Unittests stellen soll.

        Das ist schwierig zu beantworten. Selten wird wirklich alles unit-getestet (weil bei manchen Komponenten der Aufwand im vergleich zum Risiko zu hoch ist).
        Eine Testabdeckung von 70-80% würde ich in der Web-Entwicklung meist als ausreichenden Kompromiss zwischen Aufwand und Robustheit sehen. Meines Wissens wird aber z.b. im Flugzeugbau oder der Raumfahrt eine Testabdeckung von 100% (oder zumindest sehr nahe an diesem Wert) gefordert - was extrem aufwändig ist, denn die letzten Prozentpunkte Coverage machen das Test-Schreiben EXTREM zeit-intensiv.

        Letztendlich hängt es davon ab, wie hoch Du das Risiko einer bestimmten Komponente siehst, dass sie sich (durch Modifikationen usw.) irgendwann nicht mehr so verhält, wie sie soll (möglicherweise sogar völlig unbemerkt), und wie kritisch ein solcher Fehler wäre.

        Viele Grüße,
        Jörg

        1. Lieber Jörg,

          du hast mir echt super duper enorm geholfen. Ich möchte absolut nicht die Hilfe von allen andern abwerten, aber du hast 1000% auf meine Frage geantwortet.

          Gruß
          befriedigter
          T-Rex

          1. Hallo,

            du hast mir echt super duper enorm geholfen. Ich möchte absolut nicht die Hilfe von allen andern abwerten, aber du hast 1000% auf meine Frage geantwortet.

            Oh, dankeschön. Man kriegt selten so deutlich so positives Feedback. Deshalb Danke zurück!

  2. hi,

    Auf der anderen Seite, solange keine größere Logik dabei ist die etwas berechnet, würde es doch reichen hier auf der Webseite selbst die Dinge zu testen. Also wirklich Browser auf und gucken ob alles richtig angezeigt wird.

    Handarbeit?

    Wenn ich nach Artikeln bezüglich Unittests suche finde ich leider nur wie man einen Unittests schreibt (also Technisch) aber nicht was man testen sollte :(.

    Beispiel: Der Login auf einer Webseite ist zu testen. Es gibt den URL und Parameter (user, pass). Wenn ich das von Hand über den Browser mache, werde ich bei einem erfolgreichen Login zur Anwendungsseite umgeleitet und sehe: Ich bin drin.

    Das könnte ich auch automatisiert testen: Der Test schnappt sich den Location-Header der Response und wertet den Inhalt der Zielseite aus, Aber was, wenn die Inhalte dieser Zielseite mal geändert werden sollten? Der Test könnte ein falsches Ergebnis liefern und muss bei jeder Änderung überarbeitet werden und das ist schlecht.

    Idee: Sende das Testergebnis selbst im Response-Header! Ein Solcher kann entweder bei jeder Response mitgesendet werden oder nur auf Anforderung (Test-Parameter im Request). Das ist einfach zu realisieren, ebenso einfach ist es für den Unit-Test, der fragt dann nur noch den Header ab, für den Content sind "die Anderen" zuständig und wenn die mal ein CSS umbenennen, bleibt Dein Unit-Test davon unberührt ;)

    In Deinen konkreten Fall geht das auch zu machen.

    Hotti