Sie zeigen eine alte Version dieser Seite an. Zeigen Sie die aktuelle Version an.

Unterschiede anzeigen Seitenhistorie anzeigen

« Vorherige Version anzeigen Version 6 Nächste Version anzeigen »

Unit Tests mit TestNG oder JUnit und Spring Unterstützung

Nuclos unterstützt seit einiger Zeit das Schreiben von Unit Tests. Die Testunterstützung entwickelt sich jedoch nur langsam weiter, und wird (hauptsächlich) im FDM Projekt verwendet. Dort finden sich also auch die besten Beispiele, allerdings sind diese nur für Nuclos Mitarbeiter zugänglich. Diese Seite gibt (auch allen Anderen) einen Überblick, was möglich ist.

Grundsätzliches zu Unit Tests

Bei Unit Tests im engeren Sinne geht es darum, eine einzelne Methode/Klasse unabhängig vom Rest des Quellcodes zu testen. Dies ist natürlich nur dann interessant, wenn die Methode/Klasse irgend etwas tut (also kein reines Value Object ist, wie etwa eine einfache Java Bean). Normalerweise möchte man Berechnungen testen, es ist jedoch auch möglich, Methoden/Klassen zu testen, die hauptsächlich IO machen.

Ein Unit Test Framework liefert eine grundlegende Basis, die es ermöglicht, Tests auszuführen, die Testergebnisse festzustellen, zu bewerten und dem Tester anzuzeigen. Insbesondere ermöglicht es es, im Rahmen des Build Prozesses und der CI, alle Tests automatisch auszuführen.

Das bekannteste Unit Test Framework ist JUnit. Dieses wird auch von Nuclos unterstützt. Daneben erlaubt Nuclos auch das Erstellen von TestNG Tests.

Während eclipse schon mit JUnit Support kommt, muss für TestNG ein weiteres Plugin installiert werden.

Praktischer Testaufbau

Ziel von Unit Tests ist es, einzelne Klassen möglichst unabhängig voneinander zu testen. Oft werden die Testfälle, die zu einer Klasse gehören, zwar an einer anderen Stelle vorgehalten wie der 'normale' Quellcode (in maven z.B. unter src/test/java). Um aber auch das Testen von 'package-private' Methoden zu unterstützen, wird der Unit Test von Klasse A im gleichen Paket wie die Klasse A angelegt. So muss die zu testende Klasse nicht modifiziert werden und ist trotzdem weitgehend (bis auf 'private' Methoden und Felder) zugänglich.

Mock Objekte in Unit Tests

Für jeden nicht-trivialen Unit Tests benötigt man Eingabedaten, die von der zu testenden Methode/Klasse verarbeitet werden sollen. Abhängigkeiten der Testklasse zu anderen Klassen werden durch entsprechende Mock Objekte ersetzt. Dieses Mock Objekte haben die gleichen Methoden, wie die entsprechende Instanz der Abhängigkeit der Testklasse, aber alle Methoden sind so überschrieben, dass man im Testfall selbst bestimmen kann, wie sich das Mock Object verhält. (Technisch wird das in Java so gelöst, dass die Mock Klasse von der Klasse der 'richtigen' Abhängigkeit erbt. Damit sich das Ganze einfacher benutzen lässt, wird für Mock Objekte meist ein Mock Framework verwendet.)

Prinzipiell ist es möglich, auch für die Eingabedaten Mock Objekte zu verwenden. In der Praxis zeigt sich jedoch, dass es meist erheblich einfacher ist, richtige Objekte als Eingabedaten zu erzeugen, z.B. wenn es sich bei den Eingabedaten um reine Value Objekte handelt.

Nuclos unterstützt das klassische Easymock und das neuere Mockito.

Spring Injection in Unit Tests

Prinzipiell können auch Klassen getestet werden, deren Abhängigkeiten mittels Spring Injection im 'Normalbetrieb' von Spring verwaltet werden. In einem solchen Fall könnte man die Klasse selbst instanzieren und für die Injections jeweils Mock Objekte erzeugen. Das dies jedoch für sehr viele Tests erforderlich ist (da ja viele Klassen Spring Injection verwenden), wird die Sache natürlich einfacher, wenn man für diese Aufgabe auch Support hat.

Dieser Support wird von Spring-Test und Springockito geliefert und ist für das Schreiben von Tests in Nuclos verfügbar.

Die Idee hinter diesen beiden Libraries ist, dass auch zum Testen ein Spring (Bean) Context erzeugt wird, der dann während des Tests zu Verfügung steht. Aus diesen Context heraus können dann Spring Injections des Tests (wie z.B. mittels @Autowired) aufgelöst werden. Dabei kann es sich bei den Spring Beans im Context sowohl um 'echte' Implementierungen des 'Normalbetriebs' handeln als auch um Mock Objekte. (Falls man im Context viele 'echte' Spring Beans verwendet, so wendet man sich zwar etwas von dem Unit Test Gedanken ab, aber es eröffnet sich eine einfache Möglichkeit, begrenzte Integrationstests zu schreiben.)

Der große Vorteil eines Test Spring Contextes ist, dass man die zu testenden Klassen nicht verändern muss und auch keinen Boilerplate Code benötigt. Die Spring Injection funktioniert auch in den Tests weiterhin wie gewohnt, man muss 'nur' im Auge behalten, das der Test Spring Context (meist) viele Mock Objekte beinhaltet.

Nuclos und Unit Tests

Bei der praktischen Umsetzung von Unit Tests für Nuclos sind mir noch ein paar spezielle Dinge aufgefallen.

Nuclos Client, Common und Server

Da Nuclos ein Client-Server System ist, besteht es grob gesagt aus 3 Teilen:

  1. Dem Nuclos Server, der auf dem Tomcat läuft
  2. Dem Nuclos Client, der mittels Web Start gestartet wird und eine Swing GUI besitzt
  3. Code, der in Nuclos Server und Client verwendet wird (beispielsweise die Value Objekte, die zwischen Server und Client ausgetauscht werden (RPC))

Diese Struktur findet man ähnlich auch in den Nuclos Maven Modulen wieder.

Unit Tests können ihrem Wesen nach nur in einem dieser Teile laufen (da die Unit Frameworks nur einen (Java-VM) Prozess erzeugen). Daher müssen im Server alle Anfragen des Client gemockt werden, und umgekehrt im Client alle Antworten des Servers. In der Praxis ist es praktisch unmöglich, Unit Tests für Swing Klassen zu schreiben, da sich die User Eingaben (per Maus und Tastatur) nicht auf einfachen Wege mocken lassen. Darüber hinaus ist die Trennung zwischen GUI und Logik im Nuclos Client Code nur sehr rudimentär vorhanden. Da sich ferner im gemeinsamen Code (fast) nur Value Objekte befinden, lassen sich Unit Test mit vertretbaren Aufwand hauptsächlich für den Nuclos Server erzeugen.

Metadaten von BOs und Feldern/Attributen

Konkrete Test bedingen praktisch immer, dass die Metainformationen der als Eingabedaten verwendeten Objekte vorliegen. Für Nuclos bedeutet dies: Möchte ich einen Test für eine Klasse A schreiben, die als Eingabedaten Instanzen des BOs B verwendet (sei es als EntityObjectVO, MasterDataVO oder GenericObjectVO), dann muss ich die entsprechenden Felder des BOs B setzen können und die Metainformationen (MetaProvider, EntityMeta, FieldMeta) für das BO B müssen zum Testzeitpunkt abrufbar sein.

 

  • Keine Stichwörter