Allgemein ·Clean Code ·Entwicklung

Testing in SAPUI5 Teil 2 – OPA5 Tests für Benutzerinteraktionen

Im ersten Teil dieser Blogserie haben wir uns einen Mock Server erstellt, mit dem wir unsere App auch ohne einen vollständigen Service testen können.
Dieser Beitrag soll nun zeigen, wie auf Basis dieser Mock Daten OPA5 Tests geschrieben werden können.

OPA ist die Abkürzung für One Page Acceptance. OPA5-Tests sind ein SAPUI5-Feature, das auf QUnit und JavaScript basiert. Getestet werden können unter anderem Benutzerinteraktionen, die Navigation innerhalb der App sowie das Data Binding.

Wie bereits im vorherigen Beitrag erwähnt, vereinfacht diese Herangehensweise das Schreiben von Tests. Da Mock Daten sich in der Regel nicht ändern und auch keine neuen Datensätze hinzukommen sollten, können in der Testimplementierung statische Bezeichnungen oder Zahlen verwendet werden. Dies hat zudem den Vorteil, dass Tests, die nach dem OPA5 Prinzip durchgeführt werden, ausschließlich Benutzerinteraktionen testen und keine Fehler aufgrund des Datenstamms provozieren.

Ein Besonderheit der OPA5-Tests ist, dass die Asynchronität von JavaScript bzw. SAPUI5 Anwendungen durch Polling “versteckt” wird. Das bedeutet vereinfacht, es wird bei einem Aufruf nicht auf eine Antwort gewartet, sondern nach einem Status gefragt. Es gibt beispielsweise einen Test der überprüfen soll, ob alle Tabelleneinträge geladen worden sind. Es wird ein Polling Intervall gesetzt (z.B. 400ms) nach dem geprüft wird, ob Daten alle geladen worden sind. Dieser Vorgang wird solange wiederholt, bis sämtliche Bedingungen erfüllt sind.

Da OPA5-Tests auch Exceptions erkennen empfiehlt es sich, für jede View einen eigenen Test zu schreiben. Dadurch werden kritische Fehler direkt erfasst.

Für die Gliederung von OPA5-Tests werden sogenannte Journeys verwendet. Sie können genutzt werden, um die Tests zum Beispiel nach Use-Cases oder auch nach Views zu gruppieren.

Die Journeys enthalten jeweils einen oder mehrere Tests mit den Parameterobjekten Given, Then und When. Diese enthalten die arrangements, actions und assertions für den Test, welche wiederum in einem page-object gekapselt werden.

  • Given beschreibt den Ausgangszustand, beispielsweise App ist gestartet oder bestimmte Daten sind vorhanden.
  • When beschreibt Ereignisse, die durch Userinteraktionen eintreten können, wie zum Beispiel das Klicken eines Buttons oder die Eingabe von Text.
  • Then beschreibt die Veränderungen bzw den Zustand, die durch die in When beschriebenen Ereignisse erwartet werden.

Da das Aufbauen eines iFrames und der Start der App einige Sekunden dauern kann, sollte aus Performancegründen die App pro Journey nur ein mal gestartet und beendet werden. Dies sollte logischerweise innerhalb des ersten Tests bzw. das Beenden innerhalb des letzten Tests durchgeführt werden. Aus diesem Grund ist es auch empfehlenswert Tests in Journeys zu gruppieren, da bei eigenständigen Tests die App jedes mal gestartet und beendet werden müsste. Nachfolgend ein Beispiel für eine Journey.

sap.ui.require(
["sap/ui/test/opaQunit"],
function (opaTest) {
"use strict";
QUnit.module("Posts");
opaTest("Should see the table with all posts", 
function (Given, When, Then) {
     // Arrangements
     Given.iStartMyApp();
     //Actions
     When.onTheWorklistPage.iLookAtTheScreen();
     // Assertions
     Then.onTheWorklistPage.theTitleShouldDisplayTotalAmountOfItems();
});
opaTest("Should be able to load more items", 
function (Given, When, Then) {
     //Actions
     When.onTheWorklistPage.iPressOnMoreData();
     // Assertions
     Then.onTheWorklistPage.theTableShouldHaveAllEntries().
     and.iTeardownMyAppFrame();
});
}
);

Wer sich nun wundert, was es mit diesen ominösen onTheWorklistPage Objekt oder zum Beispiel der iPressOnMoreData() Methode auf sich hat, diese werden in den bereits erwähnten page-objects implementiert.
Eine Implementierung für die action iPressOnMoreData und die dazugehörige assertion würde beispielsweise folgendermaßen aussehen.

Opa5.createPageObjects({
onTheWorklistPage: {
baseClass: Common,
actions: {
     iPressOnMoreData: function () {
     	return this.waitFor({
  	     id: sTableId,
	     viewName: sViewName,
	     matchers: function (oTable) {
		return !!oTable.$("trigger").length;
	     },
	     success: function (oTable) {
		oTable.$("trigger").trigger("tap");
	     },
	     errorMessage: "The Table does not have a trigger"
	});
     }
},
assertions: {
     theTableShouldHaveAllEntries: function () {
	return this.waitFor({
	     id: sTableId,
	     viewName: sViewName,
	     matchers:  new AggregationLengthEquals({
		name: "items",
		length: 23
	     }),
	     success: function () {
		Opa5.assert.ok(true, "The table has 23 items");
	     },
	     errorMessage: "Table does not have all entries."
	});
     },

Hier ist zu sehen, dass aufgrund der Asynchronität jede action oder assertion mit einem waitFor Statement beginnt. Hier kommt das anfangs erwähnte Polling ins Spiel, welches die Bedingung überprüft und auf deren Erfüllung wartet. Der Test schlägt fehl, wenn nach einer vorgegebenen Zeit die Bedingung noch nicht erfüllt wurde.
Mit Hilfe von matchers genannten Objekten, die entweder selbst erzeugt werden können oder im Namensraum sap.ui.test.matchers zu finden sind, werden die Testbedingungen überprüft. Der matcher gibt einen booleschen Wert zurück. Je nach Rückgabewert wird entweder die success Funktion aufgerufen oder es wird eine Fehlermeldung ausgegeben.
Im vorliegenden Fall sucht der selbst erzeugte matcher nach einem DOM-Element trigger innerhalb der Tabelle. Wird dieses Element gefunden wird innerhalb der success Funktion mittels jQuery ein tap Event ausgeführt.
Danach wird die assertion ausgeführt. Assertions führen ausschließlich Überprüfungen durch, wobei es ausreichend ist, wenn nur eine Überprüfung erfolgreich ist. Zuerst steht hier wieder das waitFor Statement, gefolgt von einem OPA5 Standard matcher, welcher die Anzahl der Elemente in der Tabelle überprüfen kann.

Bei diesem Test zeigt sich ein bereits erwähnter Vorteil von Mock Daten. Die Anzahl der Elemente in der theTableShouldHaveAllEntries Funktion ist hart kodiert, wodurch Programmierarbeit gespart wird und die Methode weniger fehleranfällig macht. Das wäre bei der Nutzung eines echten Services nicht möglich, da hier die Möglichkeit besteht, dass sich Daten ändern und somit eventuell eine andere Anzahl vorhanden wäre, was den Test fehlschlagen lassen würde.

Ich hoffe dieser Beitrag dient euch als Orientierungshilfe in der OPA5- und SAPUI5-Welt.

In meinem nächsten Beitrag erkläre euch, was es mit QUnit-Tests auf sich hat! Ich hoffe ihr schaut dann wieder rein 😉

Schon gesehen?
Arbeiten als SAPUI5-Entwickler bei Acando

Schreibe einen Kommentar