JAXenter

Das Portal für Java, Architektur, Cloud & Agile

SWTBot für automatisierte SWT- und RCP-Tests

Qualität für alle

SWTBot für automatisierte SWT- und RCP-Tests

Wer kennt das nicht: Der Kunde will, dass seine Anwendung schnell fertig wird, alle Funktionen beinhaltet, preisgünstig ist und, wenn das nicht schon schwierig genug unter einen Hut zu bringen wäre, fehlerfrei soll sie auch noch sein. Das muss nicht unweigerlich in einer Katastrophe enden. Der Dreh- und Angelpunkt, diesen Erwartungen gerecht zu werden, ist die Qualität. Wenn sie nicht stimmt, dann lässt sich oft der Termin nicht einhalten und die Vorgaben können nicht vollständig erfüllt werden. Im schlimmsten Fall scheitert das Projekt und man verliert den Kunden. Verluste auf beiden Seiten sind die Folge, und die Stimmung ist im Keller. Ein gutes Qualitätsmanagement ist daher sehr wichtig. Hierzu gehört in erster Linie das gründliche Testen der Anwendung, idealerweise vor und nach jeder Änderung. Manuelle Tests benötigen zu viel Zeit und sind zudem auch noch fehleranfällig. Auf sie kann dennoch nicht ganz verzichtet werden, beispielsweise beim Thema Usability. Der Schlüssel zum Erfolg liegt darin, die Tests zu automatisieren. Dabei unterscheidet man zwischen White-Box-Tests und Black-Box-Tests. Die erste Variante setzt Kenntnisse über die Logik des Codes voraus, während man bei der zweiten eine fachliche Sicht auf die Anwendung hat. Automatisierte Tests für das User Interface (UI) müssen möglichst naturgetreu die Interaktionen von Benutzern mit dem Client simulieren, d.h. Tastatur- und Mauseingaben nachahmen. Bewährt haben sich zu diesem Zweck Testtools, die nicht über die Event-Queue der Betriebssysteme direkt gehen, sondern mit der Grafikbibliothek kommunizieren, die vom zu testenden Client verwendet wird (z.B. Swing oder SWT). Einer der Vorteile davon ist, dass die grafischen Komponenten beim Fernsteuern der Benutzeroberfläche des Clients unabhängig von ihrer relativen Position "getroffen" werden. Auch SWTBot arbeitet so. Das macht die Tests wesentlich stabiler.

Mit SWTBot ist es möglich, Tests auf eine natürliche und vertraute Art zu automatisieren. Wie bei Selenium und Jemmy werden SWTBot-Tests in Java geschrieben, anstatt sie in einer Skriptsprache visuell oder gar in XML zu entwerfen. Ausgeführt werden sie als normale JUnit-Tests (SWT) oder als SWTBot-Tests (Rich Client Platform). Das Testtool stellt Plug-ins zur Verfügung, mit denen über eine leicht verständliche API auf das UI zugegriffen werden kann, um damit Benutzeraktionen zu simulieren. Intern werden dabei Events ausgelöst, die effektiv denen eines Benutzers aus Fleisch und Blut entsprechen. Weiterhin können momentane UI-Eigenschaften wie okButton.isActive() abgefragt werden, um sie mit Erwartungen zu vergleichen. Für diejenigen, die Tests lieber in einer Skriptsprache schreiben, gibt es gute Nachrichten: Es ist geplant, JRuby, Groovy & Co. in einer kommenden Version von SWTBot zu unterstützen.

SWTBot ist ein Open-Source-Projekt und wird unter der Apache-Lizenz 2.0 zum Download angeboten. Anfang September wurde es von seinem Architekten und Chefentwickler als offizielles Eclipse-Projekt vorgeschlagen [1]. Falls das akzeptiert wird, ändert sich der Lizenztyp in Eclipse Public License 1.0, d.h. es bleibt weiterhin gratis und Open Source. Einige Worte zu den Anforderungen von SWTBot: Für die aktuelle Version 2.0 wird auf Clientseite mindestens Java 5 vorausgesetzt. Die von der zu testenden Anwendung verwendete SWT- bzw. RCP-Version sollte mindestens 3.3 sein.

Tuchfühlung

Doch bevor näher auf die Einzelheiten eingegangen wird: Wie sieht ein Test mit SWTBot im Kern überhaupt aus? In einem Beispiel (Listing 1) wird eine Adressbuchanwendung [2] in JUnit 4 mit SWTBot automatisiert getestet. Der Client wurde zuvor im Setup gestartet. Im Test wird ein neuer Kontakt in das Adressbuch hinzugefügt und geprüft, ob das tatsächliche Verhalten des UI mit der Erwartung übereinstimmt.

Listing 1
@Test
public void addContact() {
SWTBot bot = new SWTBot();
assertEquals(0, bot.table().rowCount());
assertFalse(bot.menu("Edit Contact...").isEnabled());
bot.menu("New Contact...").click();
bot.textWithLabel("Last Name").setText("Schwäbli");
bot.textWithLabel("First Name").setText("Hans");
bot.button("OK").click();
assertEquals(1, bot.table().rowCount());
assertEquals("Hans", bot.table().cell(0, "First Name"));
bot.table().select("Schwäbli");
assertTrue(bot.menu("Edit Contact...").isEnabled());
}

Die zentrale Klasse heißt so wie das Testtool selbst: SWTBot. Mit ihr greift man auf die Widgets zu. Für jedes unterstützte Widget gibt es in SWTBot eine entsprechende Wrapper-Klasse, z.B. SWTBotButton und SWTBotText. Darin befinden sich diejenigen Methoden, die das gewünschte Widget anhand von Kriterien wie dem Textattribut finden und fernsteuern. Die Grundkonstruktion besteht aus Wrappern, Findern und Matchern. Für Spezialfälle kann über eine öffentliche Instanzvariable in den Wrapper-Klassen das Widget direkt verwenden werden. Es gibt verschiedene Möglichkeiten, um auf Widgets zuzugreifen. Die einfachste ist, sie anhand des Textattributs bzw. zugehörigen Labels zu identifizieren, beispielsweise bot.text("Eddy") oder bot.textWithLabel("User Name:"). Solche Oberflächentexte können sich aber ändern, wodurch Tests dann sofort fehlschlagen würden. Bei mehrsprachigen Anwendungen hängen sie zudem von der Sprache ab, in der das UI läuft. Eine stabilere Art, um auf Widgets zuzugreifen, sind daher IDs, die ab SWTBot 2.0 unterstützt werden. Sie müssen im Client, den man testen möchte, mit Widget#setData(String,Object) gesetzt worden sein. Häufig basiert die Notwendigkeit solcher IDs auf kontextspezifischen Hilfssystemen. Dies sollte dann kombiniert werden. Um nicht mit anderen Widget-Datenobjekten ins Gehege zu kommen, wird zusätzlich ein key-Parameter verwendet. Ein Anwendungsbeispiel könnte dann folgendermaßen aussehen:

bot.buttonWithId("testkey", "okButton").click();

Anlasser

Gestartet wird das UI in der mit @BeforeClass annotierten JUnit-Setup-Methode (Listing 2). In dieser Startmethode wird sichergestellt, dass JUnit und das UI jeweils in einem separaten Thread laufen. Andernfalls kann es geschehen, dass die Oberfläche den Test blockiert, nachdem z.B. ein modaler Dialog gestartet wurde. Das lässt sich auf zwei Arten lösen: Entweder man verwendet Dialog#setBlockOnOpen(false) oder das UI wird von einem separaten Thread aus gesteuert. Letztere Variante ist die praktikablere Lösung, da sie keine Bedingungen an die Implementierung des UI voraussetzt. Zwar schützt sich SWT durch Thread Confinement [3] vor direkten Zugriffen aus einem anderen Thread, jedoch ist es möglich, mittels der Methoden Display#syncExec(Runnable) und Display#asyncExec(Runnable) Code in SWT aus einem anderen Thread heraus auszuführen. Das ist zwar etwas unübersichtlich aufgrund der vielen Runnables, wird jedoch von SWTBot intern mit der Klasse UIThreadRunnable erledigt, sodass man sich in den Tests nicht darum kümmern muss und sie gut lesbar bleiben.

Listing 2
@BeforeClass
public static void startClient() {
Runnable runnable = new Runnable() {
@Override
public void run() {
AddressBook.main(null);
}
};
Thread guiThread = new Thread(runnable);
guiThread.start();
while (Display.findDisplay(guiThread) == null) {
} // wait
}

Nach dem Start der Oberfläche kann die Klasse SWTBot instantiiert werden. Das UI wird in diesem Beispiel für alle Tests nur einmal gestartet, was in der Regel deutlich performanter ist, als sie für jeden Testfall zu öffnen und anschließend zu schließen. Der Nachteil dabei ist, dass es im Fehlerfall etwas umständlicher sein kann, den Client aufzuräumen. Denn z.B. kann ein unerwarteter modaler Dialog wie "Wollen Sie speichern?" alle nachfolgenden Tests aus der Bahn werfen. Deswegen sollte man entweder im Setup oder im Teardown das UI in einen definierten Ausgangszustand versetzen, ohne es zu schließen und neu zu starten, also gewissermaßen ein Reset Perspective. Eine langsamere, aber gründlichere Initialisierung kann gegebenenfalls über PlatformUI.getWorkbench().restart() erreicht werden.

 
Verwandte Themen: 

Kommentare

Ihr Kommentar zum Thema

Als Gast kommentieren:

Gastkommentare werden nach redaktioneller Prüfung freigegeben (bitte Policy beachten).