Nachdem letzte Woche bereits häufige Fragen im Fachgespräch rund um
das Thema Softwaretests besprochen wurden, folgen nun einige
Detailfragen zum Bereich Unit-Tests in der fünfundneunzigsten
Episode des Anwendungsentwickler-Podcasts. TL;DR Mit meinen Fragen
zu Unit Testing bereitest du dich auf dein Fachgespräch als
Fachinformatiker:in Anwendungsentwicklung vor. Du erfährst, was
eine Unit ist, wie du Tests nach Arrange, Act, Assert aufbaust und
wie du Abhängigkeiten durch Testobjekte ersetzt. Code Coverage und
Mutationstests helfen dir, Lücken in deinen Tests zu finden. Die
Testpyramide beschreibt, wie du deine Tests verteilst, wobei
schnelle, verlässliche Unit-Tests die breite Basis bilden.
Kapitelmarken [00:00:01] Hinweise zum Fachgespräch [00:00:20] Unit
Testing als Thema im Fachgespräch [00:01:24] Was ist eine Unit?
[00:03:40] Unit-Tests, Integrationstests und Systemtests [00:08:08]
Aufgaben und Beispiele von Unit-Test-Frameworks [00:10:26]
Assertions und erwartete Werte [00:11:51] Tests nach Arrange, Act,
Assert aufbauen [00:13:53] Abhängigkeiten durch Mocking ersetzen
[00:15:40] Stubs und Mocks im Detail [00:17:48] Code Coverage und
sinnvolle Testabdeckung [00:19:40] Eigenschaften guter Unit-Tests
[00:22:49] Mutationstests [00:24:12] Die Testpyramide [00:26:11]
Oberflächen automatisiert testen [00:29:08] Rückblick auf die
Fachgesprächsfragen [00:31:53] Buchtipp: Pragmatic Unit Testing in
Java 8 with JUnit [00:33:15] Buchtipp: Growing Object-Oriented
Software, Guided by Tests [00:35:42] Ankündigung des Buchclubs
[00:36:17] Newsletter und Listen zur Projektarbeit [00:36:54]
Kontakt und Fragen zum Unit Testing [00:37:20] Fragen für Episode
100 Inhalt Was ist eine Unit? Die kleinste zu testende Einheit,
meist eine Methode. Wie unterscheiden sich Unit- bzw. Komponenten-,
Integrations- und Systemtest? Unit-Test: Einzelne Komponente wird
in Isolation getestet Integrationstest: Das Zusammenspiel mehrerer
Komponenten wird getestet Systemtest: Das System als Ganzes wird
auf Funktionalität getestet Was ist ein Unit-Test-Framework? Ein
Softwareframework, das den Entwickler beim Schreiben automatischer
Tests unterstützt. Was ist eine Assertion? Eine konkrete Prüfung in
einem Unit-Test (z.B. Gleichheit von Werten). Was bedeutet AAA?
Arrange, Act, Assert Was ist Mocking? Abhängigkeiten werden durch
Fake-Objekte ersetzt, um die eigentlich zu testende Komponente in
Isolation ausführen zu können. Was ist Code-Coverage? Der Grad der
Abdeckung des Codes durch die Tests. Welche Eigenschaften sollten
Unit-Tests haben? Schnell, einfach, isoliert, wiederholbar,
verlässlich, keine Infrastruktur verwenden. Was ist ein
Mutationstest? Ein Test, der den zu testenden Code verändert, um
die Testabdeckung durch vorhandene Tests zu prüfen. Was ist die
Testpyramide? Eine Empfehlung für das Verhältnis zwischen Unit-
(viele), Integrations- (mehrere) und Systemtests (wenige). Wie kann
man Oberflächen testen? Manuell oder mit Hilfe von Frameworks wie
Selenium. Literaturempfehlungen Einen sehr schönen Einstieg in das
Thema Unit-Testing liefert Jeff Langr in seinem Buch Pragmatic Unit
Testing in Java 8 with JUnit*. Er fängt "bei 0" an und erklärt alle
wichtigen Konzepte inkl. Mocking für die Praxis. Außerdem arbeitet
er auch mit den neuen Features von Java 8 (Lambdas, Streams) um die
Tests zu optimieren. Sehr empfehlenswert! Ich lasse es meine Azubis
als Grundlagenbuch lesen und werde bald auch einen Buchclub dazu
aufnehmen. * Und wenn du ein Buch lesen möchtest, dass dir noch
einmal einen völlig anderen Blick auf das Vorgehen bzgl. der Tests
beim Schreiben einer Software ermöglicht, empfehle ich dir
wärmstens Growing Object-Oriented Software, Guided by Tests*. Die
beiden Autoren zeigen ein komplett testgetriebenes Vorgehen, das
mit Integrationstests beginnt. Ein sehr spannender Ansatz, der mit
einem großen Praxisbeispiel in Java illustiert wird. Hier habe ich
das Buch schon vor einigen Jahren rezensiert: Growing
Object-Oriented Software, Guided by Tests (Freeman/Pryce). * Links
Permalink zu dieser Podcast-Episode RSS-Feed des Podcasts
Testabdeckung – Wikipedia Regressionstest – Wikipedia
Zusammenfassung Was du unter einer Unit verstehst Wenn ich im
Fachgespräch nach einer Unit frage, geht es um die kleinste
Einheit, deren Verhalten du testen kannst. In einer
objektorientierten Sprache ist das meist eine Methode; bei
prozeduralem Code kommt eine Funktion oder Prozedur infrage. Damit
du das Verhalten prüfen kannst, muss etwas beobachtbar sein. Das
kann ein Rückgabewert sein oder ein Seiteneffekt, etwa ein Auftrag,
eine E-Mail zu versenden. Eine Assertion prüft anschließend, ob das
beobachtete Verhalten deiner Erwartung entspricht. Bei Methoden,
die innerhalb einer Klasse zusammenarbeiten, ziehe ich die Grenze
bereits beim Integrationstest. Ein einzelner Unit-Test sollte sich
auf genau ein Verhalten konzentrieren. Welche Testebenen es gibt
Unit-Test beziehungsweise Komponententest: Du prüfst eine einzelne
Einheit isoliert. Abhängigkeiten ersetzt du durch Testobjekte,
sodass der Test unabhängig von einer echten Datenbank oder einem
Netzwerk läuft. Integrationstest: Du prüfst, wie mehrere
Komponenten zusammenarbeiten. Dabei kann auch Infrastruktur
beteiligt sein, beispielsweise das Dateisystem. Systemtest: Du
prüfst die Anwendung als Ganzes über ihre Schichten hinweg. Wenn du
über eine Weboberfläche ein Benutzer:innenkonto anlegst und
anschließend prüfst, ob es in der Datenbank gespeichert wurde, ist
das ein solcher Test. Systemtests kannst du manuell durchführen
oder automatisieren. Bei Webanwendungen lässt sich dafür
beispielsweise ein Browser mit Selenium fernsteuern. Wie dir ein
Unit-Test-Framework hilft Beim Schreiben automatisierter Tests
fallen wiederkehrende Aufgaben an. Ein Test-Framework stellt dir
dafür Funktionen bereit, etwa um Werte zu vergleichen und
fehlgeschlagene Tests anzuzeigen. Dadurch musst du den
entsprechenden Code weniger oft selbst schreiben. Für Java gibt es
JUnit, im .NET-Umfeld beispielsweise NUnit. Bei Ruby steht unter
anderem RSpec zur Verfügung. Für viele Programmiersprachen
existieren passende Frameworks. Für die Sprache Natural habe ich im
Rahmen meiner Masterarbeit ein eigenes Unit-Test-Framework
geschrieben. Zu dessen grundlegenden Aufgaben gehört es, Module
aufzurufen und die Ergebnisse mit Assertions zu prüfen. Der
verfügbare Quellcode von JUnit bietet dir ebenfalls einen Einblick
darin, wie ein solches Framework aufgebaut ist. Was eine Assertion
prüft Wenn du eine Additionsmethode mit zwei Zahlen aufrufst,
erwartest du deren Summe als Ergebnis. Eine Assertion vergleicht
diesen erwarteten Wert mit dem Wert, den die Methode zurückgibt.
Bei einer Abweichung schlägt der Test fehl. Auch andere Bedingungen
lassen sich prüfen, beispielsweise ob eine Liste einen bestimmten
Wert enthält. In Frameworks begegnen dir dafür häufig die Begriffe
Expected für den erwarteten Wert und Actual für den Wert, der beim
Test herauskommt. Wie du einen Test nach AAA aufbaust Für meine
Unit-Tests folge ich dem verbreiteten Aufbau Arrange, Act, Assert,
kurz AAA: Arrange: Du bereitest das Testszenario vor. Dazu erzeugst
du beispielsweise ein Objekt der Klasse, die du testen möchtest,
und richtest benötigte Testobjekte ein. Act: Du rufst die zu
testende Methode auf. Dieser Abschnitt besteht meist aus einer
Zeile. Einen Rückgabewert speicherst du für die anschließende
Prüfung. Assert: Du prüfst, ob das Ergebnis deiner Erwartung
entspricht. Bei einer Methode mit Seiteneffekten kannst du
beispielsweise prüfen, welcher Aufruf bei einem Mock angekommen
ist. Wie du Abhängigkeiten ersetzt Wenn deine Methode einen
Mailserver anspricht, kannst du dessen Rolle im Test durch ein
Testobjekt übernehmen lassen. So prüfst du den Auftrag zum Versand
einer E-Mail, während die Methode isoliert bleibt. Unter dem
Begriff Mocking wird häufig allgemein verstanden, dass
Abhängigkeiten durch solche Objekte ersetzt werden. Im Fachgespräch
frage ich auch nach den genaueren Begriffen: Ein Stub liefert bei
einem Methodenaufruf einen festgelegten Wert zurück. Eine ersetzte
Datenbankklasse kann beispielsweise immer die Zahl 7 liefern, mit
der deine Methode weiterrechnet. Ein Mock zeichnet auf, welche
Methoden bei ihm aufgerufen wurden. Anschließend kannst du unter
anderem die übergebenen Parameter und die Zahl der Aufrufe prüfen.
Beim Mailversand lässt sich so feststellen, ob die Versandmethode
mit der vorgesehenen Empfänger:innenadresse aufgerufen wurde.
Mocking-Frameworks wie Mockito für Java oder Moq für .NET
unterstützen diese Aufgaben. Viele davon können sowohl feste
Rückgabewerte liefern als auch Aufrufe überprüfen. Für dein
Fachgespräch solltest du die Begriffe auseinanderhalten können. Was
Code Coverage aussagt Wenn deine Tests laufen, kann ein Werkzeug
erfassen, welche Zeilen deines Produktivcodes ausgeführt werden.
Der Anteil dieser Zeilen heißt Code Coverage oder Codeabdeckung.
Werden von 100 Codezeilen 95 durchlaufen, beträgt sie 95 Prozent.
Eine hohe Abdeckung ist ein sinnvolles Ziel. Dabei solltest du auch
Sonderfälle berücksichtigen, damit deine Tests möglichst viele
Fehler finden können. Viele Tests für denselben Fall lassen andere
Fälle weiterhin offen. Für pauschal angestrebte 100 Prozent halte
ich den Aufwand in der Praxis für unangemessen, etwa bei
automatisch erzeugtem Boilerplate-Code wie einer toString-Methode.
Solche Stellen vollständig abzudecken, kann viel Zeit beanspruchen.
Welche Eigenschaften deine Unit-Tests haben sollten Schnell: Ein
Unit-Test sollte im Millisekundenbereich laufen. Dadurch kannst du
deine Tests nach kleinen Codeänderungen ausführen und zeitnah
erkennen, ob etwas kaputtgegangen ist. Einfach: Halte den Testcode
kurz und verständlich. Auch hier gelten Regeln für guten Code, etwa
das Single Responsibility Principle. Als Einstieg empfehle ich eine
Assertion pro Testmethode; davon weiche ich gelegentlich ab. ...
Kommentare (0)
Melde dich an, um einen Kommentar zu schreiben.