Kannst du mal ruhig sein? Ich habe keine Zeit, dich zu ignorieren!
Das Großmaul ist sehr mitteilungsbedürftig. Wenn er läuft, erzeugt er massiv Logausgaben, schreibt die Kommandozeile voll und möchte keine Exception unerwähnt lassen. Es könnte ja jemanden interessieren.
Ich bitte um Feedback ….
Logging-Frameworks sind eine schöne Erfindung. Richtig umgesetzt, stellt Logging eine brauchbare Alternative zum Debugging dar. Logmeldungen sind leicht einzubauen und anhand des Quellcodes gut nachvollziehbar. Im Gegensatz zum Debugging muss man sich auch nicht durch baumartige Datenstrukturen klicken oder Breakpoints setzen.
Werden die Logausgaben in Textdateien geschrieben, so entsteht im Produktivbetrieb zudem ein schönes Aktivitätsprotokoll. Werden die Informationen in eine Datenbank geschrieben, können wir die Logs mit Datenbankabfragen auch noch effektiv auswerten.
Hier meldet der Tomcat: die Leitung ist belegt!
Setzen wir zudem die Log-Levels (Trace, Debug, Info, Warning, Error) auch noch sinnvoll ein, erhalten wir die Möglichkeit, das Programmverhalten auf verschiedenen Ebenen zu betrachten. Durch eine simple Konfiguration kann das System sogar in die Lage versetzt werden, im Fehlerfall E-Mails an die DevOps zu verschicken oder automatisch Tickets im Bugtracking-System zu eröffnen (wenn man dieses denn möchte).
Aber bitte nur konstruktive Kritik
Insgesamt also eine super Sache. Warum also nicht auch unsere Unit-Tests loggen?
Nun, zunächst einmal holen wir uns mit dem Einsatz einer Logging-Komponente einige potenzielle Nachteile in unsere Tests. Es handelt sich um eine zusätzliche Komponente, die fehlschlagen kann. Allein schon die unbedachte Abfrage einer Objekteigenschaft in einer Logausgabe birgt die Gefahr einer vergessenen NullPointer-Prüfung: unser Test schlägt fehl.
Die Logausgaben werden zudem in dieselbe Konsole geschrieben wie das Testprotokoll. Durch Logging verwässern wir also unser Testprotokoll. Konsolenausgaben zeigen außerdem ein sehr schlechtes Laufzeitverhalten: sie sind langsam. Ein Test, der mit jeder Iteration einer for-Schleife die Konsole vollsabbelt, bremst den Testzyklus hemmungslos aus.
Unter Umständen verwenden wir auch ein Testframework, das keine sichtbaren Logausgaben produziert. Wer sich hier auf seine Logausgaben verlässt, dürfte ein langes Gesicht machen…
Und schließlich müssen wir uns auch die Frage nach dem dokumentarischen Zweck stellen: müssen wir unsere Testergebnisse protokollieren? Reicht uns nicht die Information, dass eine Funktion aktuell frei von Fehlern ist? Ich tippe auf letzteres ….
Schlaumeiern kann ich selber ….
Deine Tests sollten nach Möglichkeit die folgenden Kriterien erfüllen: Sie sollten schnell laufen und nachvollziehar sein. Der Fokus soll klar auf der zu testenden Komponente liegen. Diese Komponente soll auch das einzige sein, was fehlschlagen kann. Und die Testbeschreibung soll den Test umfassend beschreiben.
Das Jasmine Testframework für JavaScript beispielsweise verwendet BDD-artige Testbeschreibungen:
describe('The Date input field', function() { .... describe('that is mandatory and requires a date in the past', function() { it('should yield an error if a future date is entered', function() { dateField.sendKeys('26/08/2020'); dateFieldContainer.shouldShowErrorMessage(); }) }) });
Im Testprotokoll sieht das dann in etwa so aus:
The Date input field that is mandatory and requires a date in the past should yield an error if a future date is entered. ... Test failed! in dateInputSpec.js, line 45
Eigentlich brauchst du jetzt keine Logausgaben mehr, oder?
Und was haben wir nun gelernt?
Das ist einfach: Produktivcode loggen: ja. Tests loggen: lieber nicht. Das ist aber auch nicht so schlimm. Wenn du die Tests ordentlich schneidest und noch eine ordentliche Testbeschreibung dazu gibst, dann ist die Sache schon geritzt.
Eine schöne Sammlung zu Best Practices beim (J)Unit Testing findest du übrigens hier:
Wir wünschen dir viel Spaß beim Schreiben aussagekräftiger Testfälle!