Unit Tests bilden das Fundament der Pyramide
Unit Tests sind simple, einfach zu automatisierende Tests, welche die einzelnen Bestandteile einer Software (Units) isoliert voneinander prüfen. Abhängigkeiten werden durch Mocks ersetzt, um jegliche Einflüsse und Folgefehler anderer Units von der getesteten Einheit fernzuhalten. Bei Unit Tests handelt es sich um White-Box-Tests, wir kennen also die innere Funktionsweise unseres Systems und dessen Bestandteile.
Diverse Tools können bei automatisierten Unit Tests unterstützen. Hier die zwei wichtigsten:
- Test-Frameworks bilden die Grundlage, um Tests direkt innerhalb der Code-Base zu schreiben und auf immer gleiche Weise ausführen zu können. Tests werden wiederholbar und automatisierbar. Empfehlung: In der Java-Welt haben sich JUnit und TestNG bewährt.
- Mocking-Frameworks erlauben das einfache Mocken von Units. Abhängigkeiten werden somit automatisiert und durch eine Dummy-Implementation ersetzt. Empfehlung: Eines der meistgenutzten Mocking-Frameworks der Java-Welt ist Mockito, welches hervorragend mit den obengenannten Test-Frameworks zusammenarbeitet.
Integrationstests ermöglichen die fehlerfreie agile Integration
Getestete und korrekt funktionierende Komponenten (Units) sind die Voraussetzung für Integrationstests, bei denen das korrekte Zusammenspiel voneinander abhängiger Services eines Gesamtsystems sichergestellt wird. An dieser Stelle wird der einzelne Service als Black-Box betrachtet. Es zählt also nicht mehr die korrekte innere Funktionsweise, die bereits durch die Unit Tests sichergestellt wurde. Stattdessen betrachten wir die Interaktion unseres Service mit anderen Services über die jeweiligen Schnittstellen.
Auch hier bieten sich diverse Tools als Unterstützung an:
- Integrationstest-Frameworks erlauben es mit einfach automatisierbaren Tests sowohl Clients als auch Umsysteme zu simulieren. Dadurch lassen sich bei Aufrufern und aufgerufenen Services Prüfungen vornehmen und die korrekte Verhaltensweise des zu testenden Service sicherstellen, ohne dass die realen Umsysteme verfügbar sein müssen. Dies nimmt den Integrationstests viel von ihrer eigentlichen Komplexität und macht sie schnell und automatisiert ausführbar. Empfehlung: Citrus hat sich für diese Aufgabe bei mehreren Kunden bewährt. Das Spring-basierte Framework ist Open Source und unterstützt von REST, SOAP, JMS und Kafka bis hin zu FTP und Mail-Servern alle Protokolle, die das Herz eines Java Entwicklers begehrt.
- Contract-Test-Frameworks basieren auf einem Vertrag zwischen einem konsumierenden (Consumer) und einem produzierenden System (Producer). In diesem Vertrag wird Schema und Verhalten der Schnittstelle des Producers festgehalten. Darauf aufbauend können Mocks für den Consumer und Tests für den Producer generiert werden. Ein Consumer ist dadurch nicht mehr auf die Verfügbarkeit des realen Producers angewiesen. Gleichzeitig wird der Producer durch die generierten Tests darüber informiert, wenn eine Änderung den Vertrag bricht, sprich einen Breaking Change für den Consumer herbeiführen würde. Empfehlung: Wer mit verschiedenen Sprachen und Frameworks arbeitet, ist mit dem Pact-Framework hervorragend bedient. Es unterstützt von Java und JavaScript bis Perl und Go jede erdenkliche Technologie und bietet nützliche Zusatzfeatures wie den Pact Broker, der das Verwalten von Contracts erlaubt. Wer spezifisch mit dem Java Spring-Framework arbeitet, sollte sich aber auf jeden Fall Spring Cloud Contracts ansehen, da es perfekt in Spring integriert ist.
Pipelines führen Tests automatisch aus
Nachdem die Tests entsprechend der Testpyramide geschrieben sind, geht es nun darum diese automatisch auszuführen und damit zu verhindern, dass ein ungetesteter und potenziell fehlerhafter Code in den produktiven Build gelangt. Auch hier unterstützen verschiedene Tools:
- Build-Management-Tools wie Maven oder Gradle führen beim Bauen der Software die Tests automatisch aus und schliessen den Build nur dann erfolgreich ab, wenn auch alle erfolgreich durchgelaufen sind.
- CI/CD-Tools und Automationsserver ermöglichen es, Tests und Builds an einem zentralen Ort automatisch auszuführen und den fertigen Build mittels festgelegter Schritte in einer Pipeline sofort auszuliefern und produktiv zu schalten. Beispiele solcher Tools sind Jenkins, GitLab CI/CD, Atlassian Bamboo oder Cloud Lösungen wie Azure DevOps Pipelines (Abbildung 3).