tech-trends

// 02-08-2021

Der Test von Software in der Entwicklung nimmt immer mehr an Bedeutung zu. Für jede Teststufe der Testpyramide gibt es Frameworks, wie zum Beispiel Mockito für den Komponententest oder RestAssured und Karate zum Testen von Web-Schnittstellen. Diese Frameworks leisten allesamt gute Arbeit, jedoch haben wir eine Schwachstelle bei Integrationstests, insbesondere für Backend-Services, erkannt: Die meisten Frameworks sind hier sehr schwergewichtig. Hier kommt unser Open-Source Projekt “CheetUnit” ins Spiel, welches wir im Rahmen unseres Learning-Fridays umgesetzt haben.

Was ist CheetUnit?

CheetUnit ist ein Framework zum Implementieren und Ausführen von integrativen Tests in Java EE Applikationen. Hierzu kombiniert CheetUnit die Leichtgewichtigkeit von Unittests mit der Macht des Java EE Applikationsservers. CheetUnit-Tests werden clientseitig als JUnit-Tests implementiert und ausgeführt, wobei serverspezifischer Code zum Server transferiert und dort ausgeführt wird. Das Ergebnis des serverspezifischen Codes  wird letztendlich wieder zum Client (JUnit Test) geschickt, wo dieser validiert wird. 

Genauere Informationen, wie CheetUnit funktioniert und wie du es verwenden kannst, findest du direkt in der CheetUnit-Dokumentation. CheetUnit ist auch auf Maven Central released: CheetUnit auf Maven Central

Warum CheetUnit? 

Wie sehen wir den Zweck der Integrationstests? Mit Integrationstests wollen wir überprüfen, dass das System korrekt funktioniert, wenn es auf dem Applikationsserver läuft und an verschiedene Umsystemen und Datenbanken eingebunden ist.

Hätte jedes Service eine öffentliche Schnittstelle (z.B. REST), wäre es einfach: Wir schicken eine Testanfrage an das System und kontrollieren, ob die erwartete Antwort zurückkommt. Mithilfe von Datenbank-Tools (z.B. DB-Unit) lässt sich feststellen, ob der Datenbankstand nach dem Serveraufruf korrekt ist.

Aber wie kann man interne Services testen, welche keine Webschnittstelle haben? Welche Frameworks stehen zur Verfügung?

Karate ist besser für API-Tests geeignet. Cucumber hat eher mit Akzeptanztests zu tun. Arquillian hat in manchen Situationen Nachteile, die das Testen aus unserer Sicht etwas schwieriger machen: Wenn das System groß ist oder aus mehreren Subsystemen besteht, kann der Zusammenbau des Deployments nicht einfach sein. Ein weiterer Nachteil von Arquillian ist die Performance, sobald das Projekt ein größeres Ausmaß annimmt.

Sollten wir lieber Microservices verwenden? Dann ist das Problem gelöst. Wir wissen jedoch, dass es aus verschiedensten Gründen nicht immer möglich ist. Und es gibt auch bestehende Applikationen, die man warten und weiterentwickeln muss.

Was könnte man einsetzen, wenn man Tests gegen eine bereits installierte Applikation ausführen will? Wir brauchten so ein Tool. Und nun haben wir CheetUnit!

Wie arbeite ich mit CheetUnit? 

Mit CheetUnit testet man eine laufende Applikation, die bereits auf einem Application-Server deployt ist. Wir gehen also davon aus, dass die Domain-Logik, Domain-Services und DAOs bereits vorhanden sind.

Was wir eigentlich wollen, ist ein Service remote auszuführen und das Ergebnis zu validieren. Mit CheetUnit ist es möglich, auch wenn der Service keine Webschnittstelle zur Verfügung stellt.

Zum Beispiel: wir wollen das Service HelloWorldService#getHelloWorld  testen. Zuerst schreibt man eine Invoker-Klasse. In dieser Klasse kann man das zu testende Service injecten. Der Invoker dient in dem Fall als Proxy.

public class HelloWorldServiceInvoker extends
BaseServiceInvoker {

@Inject
private HelloWorldService service;

public String getHelloWorld() {
return service.getHelloWorld();
}
}

Der nächste Schritt wäre einen Test zu schreiben. Als Erstes muss der Invoker richtig initialisiert werden. Dafür steht eine Hilfsmethode zur Verfügung:

HelloWorldServiceInvoker service =
CheetUnit.createProxy(HelloWorldServiceInvoker.class);

Nun schreibt man einen Test, wo das Service am laufenden Server über den Invoker aufgerufen wird.

class HelloWorldServiceIT {

private static HelloWorldServiceInvoker service;

@BeforeAll
static void beforeAll() {
service =
CheetUnit.createProxy(HelloWorldServiceInvoker.class);
}

@Test
void getHelloWorld() {
String result = service.getHelloWorld();
Assertions.assertEquals(``Hello World!``, result);
}
}

Unsere Ziele …

  • CheetUnit soll Open Source und in kommerziellen Projekten verwendbar sein.
  • CheetUnit soll in Maven Central zur Verfügung stehen.
  • CheetUnit soll einfach zu verwenden und gut dokumentiert sein.

… und Erfolge 🍻

Bei CheetUnit handelt es sich um ein Nachfolgeprojekt von WarpUnit, welches bereits ein ähnliches Konzept implementiert. Jedoch wurde bei WarpUnit eine ordnungsgemäße Projekt- und Code-Lizenzierung verabsäumt, weshalb aus urheberrechtlichen Gründen das Projekt so in dieser Form nicht weiterentwickelt werden konnte. Um den Open-Source-Gedanken bestmöglichst zu wahren, durften wir diesen Fehler also nicht wiederholen. Daher haben wir uns für die Apache 2.0 Lizenz entschieden, durch welche CheetUnit in allen Software-Projekten ohne Restriktionen eingesetzt werden kann.

Damit nicht nur der Source-Code der Community zur Verfügung steht, sondern auch die gebauten Software-Artefakte, haben wir CheetUnit in das Maven Central Repository released. Auf diesem Wege kann jedes Projekt, welches Maven oder Gradle einsetzt, CheetUnit nutzen. 

Wir glauben an das Potenzial von CheetUnit und Open-Source. Damit nicht nur wir CheetUnit nutzen und weiterentwickeln können, war es uns wichtig eine gute Dokumentation zu erstellen. Beginnend bei JavaDoc, bis hin zu einem eigenen Getting Started Beitrag. 

GitHub – 🏡 von Open-Source und CheetUnit

Bereits zu Beginn unseres Projektes war uns klar, dass GitHub DIE Plattform für Open-Source Projekte ist. Mit der Zeit konnten wir immer mehr Features entdecken, welche wir dir kurz vorstellen möchten.
Neben einem Git-Repository- und Ticketing-System bietet GitHub auch diverse Dokumentationsmöglichkeiten. Zuerst starteten wir mit einem Wiki. Relativ schnell war uns klar, dass wir unsere Dokumentation mittels GitHub-Pages umsetzen möchten. Neben HTML unterstützt GitHub-Pages auch diverse Jekyll Themes. Innerhalb der Themes kann die Dokumentation in der wohl unter EntwicklerInnen beliebtesten Markup-Sprache umgesetzt werden, nämlich Markdown.

Auch in Richtung Automatisierung und CI/CD-Pipelines hat GitHub eine Antwort parat, nämlich GitHub Actions. Mit GitHub Actions kann mit wenigen Klicks eine Pipeline aufgesetzt werden, welche je nach definiertem Trigger den Code auscheckt und baut. So kann beispielsweise nach jeden Push in den Main-Branch festgestellt werden, ob das Projekt noch baut und ob noch alle Unit-Tests erfolgreich durchlaufen. Für integratives Testen kann mittels Docker eine eigene Datenbank-Instanz oder ein eigener Server gestartet werden. 

Unsere Learnings

Im Rahmen von CheetUnit konnten wir viel Neues lernen. Unsere Learnings teilen sich in zwei Themengebiete auf, nämlich jene rund um die technische Umsetzung von CheetUnit und Themen rund um Open-Source. 

Technische Herausforderungen wie Reflection und Classloading innerhalb der JVM sind uns vertraut geworden und konnten wir meistern. Neben den technischen Aspekten haben wir uns viel mit Open-Source beschäftigt. Die Learnings zu diesem Themengebiet sind in Form eines eigenen Blog-Beitrags festgehalten – sehr lesenswert! 

Dein Interesse für CheetUnit wurde geweckt? Du möchtest CheetUnit nutzen oder gar bei der Weiterentwicklung maßgeblich beteiligt sein? Werde Teil der CheetUnit-Community!

// Autor

Christian, Egor, Patrick (LF-Team)