Freitag, 6. November 2009

Testbare Oberflächen

In seinem schönen Vortrag Auf dem Kreuzzug gegen Fehler in Software bei der ersten Swiss Testing Night vom 12. Juni des Jahres berichtet Jerome Müller von Entwicklern, die buchstäblich alles können und an so gut wie alles denken. Wenn man sie nach dem Entwurf ihrer Applikation fragt, geben sie so schlaue Dinge von sich wie:
Der Business Layer benutzt dann den Persistence Layer. Den Persistence Layer haben wir, um die Datenbankunabhängigkeit zu gewährleisten. Und den Business Layer können wir auf verschiedenen Maschinen deployen und so skalieren.


Die Software wird für alles designed! Wechsel von Oracle auf XML Files? Kein Problem! Wechsel von Windows auf Linux? Kein Problem! Anstelle der Logfiles sollen die Logeinträge an einen Webservice geschickt werden? Piece of cake!


Aber, so Müller, auf eines kann der Entwickler nicht antworten, nämlich auf die Frage,
welche Konzepte er eingebaut hat, dass die Applikation “testbar” wird. Kann ich jede Bildschirmmaske innerhalb von 3s öffnen und einstellen, in welchem Zustand die angezeigten Daten sind, damit ich das UI Verhalten testen kann? Wie erstelle ich Testdaten? etc.


Die Antwort ist leider meistens sehr kurz und kann in Null Worten zusammengefasst werden.


Um mich diesem Vorwurf nicht auszusetzen, habe ich für unser aktuelles BSP-Projekt ein Programm geschrieben, das genau dieses kann: Jedes Bild - und das heisst in unserem MVC-Framework: jeden Panel - per Mausklick aufrufen.

Der Report hilft nicht nur beim Testen des UI-Verhaltens, sondern zeigt auch ein Protokoll der HTML-Codeprüfungen durch Dave Raggetts HTML Tidy-Programm an, so dass man z.B. Schachtelungsfehler im Quellcode leicht erkennt.

Das Programm ruft nach Eingabe einer BSP-Applikation und eines in dieser Applikation gemäss der Flow Logic definierten Panels über einen internen HTTP-Request genau diesen Panel auf und zeigt drei Aspekte des Bilds: Die Browsersicht mit Hilfe eines eingebetteten HTML Viewer Controls, sowie den HTML-Quellcode und das Protokoll der Prüfungen von HTML Tidy. Hier ein Screenshot mit einer unserer "Hallo Welt"-Applikationen des MVC-Frameworks:



Die Bilder, die in obigem Testprogramm angezeigt werden, sind immer mit denselben Testdaten gefüllt und unabhängig von den tatsächlich im System gepflegten Stamm- oder Bewegungsdaten. Wie ist dies möglich?

Ein kleiner Eingriff im MVC-Framework ermöglicht den Kunstgriff, statt der "produktiven" Models von der Geschäftslogik abgekoppelte Subklassen zu verwenden, die ein Tagging Interface zif_testdata implementieren. Die Applikationsparameterstruktur zmvc_param definiert unter anderem die Frameworkklasse. Das ist eine Implementierung des Interfaces zif_mvc_framework, die dafür zuständig ist, der Applikation die zu verwendenden Models, Controller und Views zu nennen. Die Standard-Implementierung zcl_mvc_framework entnimmt diese Informationen der Datei config.xml, die im MIME-Verzeichnis der BSP-Applikation hinterlegt ist. Im Entwicklungssystem habe ich statt dieser Standard-Implementierung eine alternative Implementierung zcl_mvc_framework_test hinterlegt:



Diese greift an genau einer Stelle ein: Bei der Ermittlung der Models. Sie liefert statt des Models selbst eine Subklasse zurück, die das Interface zif_testdata implementiert - falls eine solche existiert. Die einzige Stelle mit abweichender Logik ist demnach die Methode get_model():

method zif_mvc_framework~get_model.

es_model = go_framework->get_model( iv_model_id ).

* Modell ersetzen durch Testklasse
replace_class_by_testclass( changing cs_model = es_model ).

endmethod.

Die Daten, die das User Interface benötigt, werden nun in diesen Testmodels fix aufgebaut. So sieht das UI im UI-Testmodus immer gleich aus und kann mit periodisch laufenden automatischen Tests abgesichert werden, die z.B. verifizieren, dass das HTML-Dokument die erwartete DOM-Struktur hat.

Das Programm HTML Tidy kann übrigens problemlos im ABAP-Stack aufgerufen werden, da es einen ABAP-Wrapper gibt: Die Klasse cl_htmltidy. Wenn go_tidy eine Objektvariable dieser Klasse bezeichnet, kann man HTML Tidy zum Ladezeitpunkt seiner Klasse oder seines Reports oder zum Erzeugungszeitpunkt seines Objekts wie folgt konfigurieren:
go_tidy = cl_htmltidy=>create( ).

define _set_option.
call method go_tidy->set_option
exporting
option = &1
value = &2.
end-of-definition.

_set_option:
'indent' 'auto',
'indent-spaces' '2',
'input-encoding' 'utf8'. " ! Muss so geschrieben werden (nicht "utf-8")

Beim Aufruf ist zu beachten, dass die Klasse cl_htmltidy ihr Input immer im UTF-8-Format erwartet und auch die Ergebnisse in diesem Format zurückgibt. Man muss also aus dem HTML-Code zunächst mit der Konvertierungsklasse cl_abap_conv_out_ce einen XSTRING erzeugen, der den Text in der Zeichencodierung UTF-8 enthält. Erst danach kann man HTML Tidy aufrufen:
* ---
form get_tidy.

data: lv_in type xstring,
lo_error type ref to cx_root.

statics: so_conv type ref to cl_abap_conv_out_ce.

if so_conv is not bound.
so_conv ?= cl_abap_conv_out_ce=>create( encoding = '4110' ).
endif.

try.
call method so_conv->convert
exporting
data = gv_code
importing
buffer = lv_in.

call method go_tidy->repair
exporting
input = lv_in
diagnostics = 'X'
importing
* --> In einem TextEditControl anzeigen:
errors = gv_tidy.
catch cx_root into lo_error.
zcl_messages=>show_exception( lo_error ).
endtry.

endform. "tidy_get

Tidy lässt sich nicht nur wie hier zur Validierung von Webseiten eisetzen, sondern kann auch HTML-Code in XHTML-Code konvertieren. Dafür gibt es mancherlei Nutzanwendungen: Tidy könnte ein bisschen dabei helfen, den Viewcontent eines Projekts oder einer Applikation von HTML auf XHTML zu migrieren. Auch lässt sich Tidy durch Kombination mit dem in ABAP eingebauten XML-Parser als HTML-Parser einsetzen, so dass man auch den HTML-DOM einer Webanwendung mit ABAP-Mitteln untersuchen kann.

Keine Kommentare :