Zum Inhalt springen

Suchen...

Webservices testen: Anfragen generieren, Antworten prüfen

Webservices testen mit WSDLTest: Testdaten kommen aus dem WSDL-Schema, Vorbedingungen steuern Anfragen, Nachbedingungen prüfen Antworten.

• • Aktualisiert: • 24 Min. Lesezeit
Linienzeichnung zweier Serverschränke, die sich gegenüberstehen und durch Doppelpfeile mit einem Häkchen in der Mitte verbunden sind.

Warum Webservices schwer zu testen sind

Als größtes Hindernis für den Einsatz von Webservices galt lange, sie zu testen. Wer Webservices testen will, muss ihre Nutzung simulieren: Anfragen automatisch erzeugen, Antworten schnell und zuverlässig automatisch prüfen. Harry Sneed und Shihong Huang haben dafür das Werkzeug WSDLTest entwickelt. Es nutzt eine Testdaten-Beschreibungssprache aus Vor- und Nachbedingungen (Pre- und Post-Condition-Assertions), um WSDL-Anfragen zu erzeugen und WSDL-Antworten zu validieren.

WSDLTest gehört zu einem größeren Werkzeugsatz namens DataTest, mit dem sich Systemtestdaten erstellen und verarbeiten lassen. Dieser Beitrag stellt Architektur und Funktionen des Werkzeugs vor und berichtet, welche Erfahrungen sein Einsatz in einem E-Government-Projekt gebracht hat.

Den Beitrag habe ich 2007 gemeinsam mit Harry Sneed geschrieben. Er beschreibt SOAP-Webservices mit WSDL-Schnittstellen. Heute laufen Schnittstellen häufiger als REST-APIs, das Prinzip aus erzeugten Anfragen und automatisch geprüften Antworten ist geblieben.

Welche Rolle spielen Webservices in einer serviceorientierten Architektur?

Webservices sind die wiederverwendbaren Komponenten einer serviceorientierten Architektur (SOA). Für die IT wurden sie immer wichtiger, weil Unternehmen ihre Systeme flexibler machen wollten, um schnell auf Veränderungen im Geschäftsumfeld zu reagieren. Bringt ein Mitbewerber einen neuen Marketingansatz, muss das eigene Unternehmen in kurzer Zeit nachziehen können. Die Anpassungsfähigkeit der IT ist überlebenswichtig geworden.

Neue Gesetze verschärfen den Druck. Regelwerke wie der Sarbanes-Oxley Act oder Basel II müssen innerhalb von Monaten umgesetzt sein, zu einem festen Termin, der oft nah ist. Aufschieben geht nicht.

Unter diesem Zeitdruck lassen sich keine lang laufenden Projekte mehr planen. Gefragt ist eine funktionierende Lösung in begrenzter Zeit. Die setzt wiederverwendbare Komponenten voraus, die sich in einem Standard-Framework zu einem kundenspezifischen Geschäftsprozess zusammenstecken lassen. Dieses Framework ist sehr oft eine serviceorientierte Architektur, wie IBM, Oracle und SAP sie anbieten.

Die Komponenten sind die Webservices. Den Geschäftsprozess darüber beschreibt die Prozesssprache BPEL. Den Klebstoff, der den Geschäftsprozess an die Webservices bindet und die Webservices untereinander verknüpft, liefert die Web Service Description Language (WSDL).

Die Webservice-Komponenten selbst haben verschiedene Herkunft. Einige werden gekauft, einige aus der Open-Source-Community übernommen, einige neu entwickelt. Andere stammen aus bestehenden Softwaresystemen und werden für die neue Umgebung recycelt, in der Regel, indem man sie kapselt.

Warum muss man Webservices testen?

Weil niemand garantieren kann, dass eine Webservice-Komponente so arbeitet wie erwartet, ganz gleich, woher sie kommt. Auch gekaufte Komponenten passen womöglich nicht genau zur Aufgabe, und wenn sie nicht kompatibel sind, drohen schwere Interaktionsfehler. Recycelte Komponenten können noch schlimmer sein. Legacy-Programme enthalten oft versteckte Fehler, die sich in ihrem ursprünglichen Kontext gegenseitig ausgleichen. Wandern sie in eine andere Umgebung und erfüllen dort eine etwas andere Funktion, kommen diese Fehler plötzlich an die Oberfläche. Mit Open-Source-Komponenten kann das genauso passieren.

Perry und Kaiser haben gezeigt, dass die Korrektheit einer Komponente in einer Umgebung nicht für eine andere Umgebung gilt. Komponenten müssen deshalb in jeder Umgebung, in der sie wiederverwendet werden, neu getestet werden.

Die besonderen Probleme beim Testen von Webanwendungen hat Nguyen beschrieben. Die verteilte Architektur aus Webbrowser, Webserver, Datenserver, Middleware, Applikationsserver usw. bringt viele neue Fehlerquellen mit. Die vielen möglichen Interaktionen zwischen den Komponenten, kombiniert mit vielen unterschiedlichen Parametern, erhöhen die Zahl der nötigen Testfälle und damit die Testkosten. Bewältigen lassen sich diese Kosten nur mit Testautomatisierung.

Selbst entwickelte Dienste haben dasselbe Zuverlässigkeitsproblem wie jede neue Software. Sie brauchen umfangreiche Tests auf Unit-, Komponenten- und Systemebene. Sneed und Huang (Journal of Software Maintenance and Evolution 19(5), 2007) beziffern die Fehlerrate neu entwickelter Software auf 3 bis 6 Fehler pro 1000 Anweisungen, die vor dem Produktivbetrieb gefunden und beseitigt werden müssen. Ein wesentlicher Teil davon geht auf falsche Annahmen der Entwickler über die Aufgabe und das Verhalten der Umgebung zurück. Solche Fehler deckt nur ein Test in der Zielumgebung auf, mit Daten von Menschen, die eine andere Sicht auf die Anforderungen haben. Das ist der Hauptgrund für unabhängige Tester.

Webservices sollten deshalb einen unabhängigen Testprozess durchlaufen, einzeln und im Zusammenspiel. Der Prozess sollte klar definiert und durch Werkzeuge unterstützt sein, damit er schnell, gründlich und transparent ist. Transparenz zählt beim Testen von Webservices besonders, weil Testfälle nachvollziehbar und Zwischenergebnisse prüfbar sein müssen. Wegen der Menge an Testdaten müssen Eingaben automatisch erzeugt und Ausgaben automatisch validiert werden. Kombinationen repräsentativer Testdaten sorgen für eine hohe funktionale Abdeckung, der Abgleich mit den erwarteten Ergebnissen für einen hohen Grad an Korrektheit.

Welche Werkzeuge gab es für den Test von Webservices?

Als WSDLTest entstand, gab es Werkzeuge für den Test von Webservices in großer Zahl. Das Problem war ihre Qualität. Die meisten waren Neuentwicklungen und noch nicht ausgereift. Sie ließen sich schwer an lokale Gegebenheiten anpassen und verlangten, dass Anwender Daten über die Oberfläche des Web-Clients eingeben. Der Test über die Benutzeroberfläche ist aber nicht das wirksamste Mittel, wie R. Martin in einem Beitrag im IEEE Software Magazine hervorhebt. Er schlägt einen Testbus vor, der die Oberfläche umgeht und die Dienste direkt testet. Diesen Weg sind Sneed und Huang gegangen.

Ein typisches Werkzeug war damals QuickTest Professional von Mercury. Der Anwender füllte eine Webseite aus und schickte sie ab, das Werkzeug verfolgte die Anfrage dann vom Client durch das Netzwerk, indem es die SOAP-Nachricht instrumentierte. Die Nachricht wurde bis zu dem Webservice verfolgt, der sie verarbeitete; rief dieser einen weiteren Webservice auf, folgte das Werkzeug auch diesem Aufruf. Den Inhalt jeder WSDL-Schnittstelle hielt es in einer Trace-Datei fest. So konnte der Tester den Weg einer Anfrage durch die Architektur nachvollziehen und die Nachrichteninhalte in verschiedenen Verarbeitungsphasen untersuchen.

Parasoft bot eine ähnliche Lösung an. Dort startete allerdings kein Web-Client die Anfragen, sie wurden aus den in BPEL geschriebenen Geschäftsprozessen erzeugt. Das ergab größere Datenmengen und simulierte zugleich die Bedingungen des Betriebs, denn die meisten Webservice-Anfragen sollten ohnehin von den BPEL-Skripten kommen, die die Geschäftsprozesse steuern. Was fehlte, war die Prüfung der Antworten. Sie mussten von Hand gesichtet werden.

Zu den Pionieren beim Web-Test gehörte die Firma Empirix. Mit ihrem Werkzeug eTester simulierten Tester die Geschäftsprozesse über die Web-Clients. Die Anfragen wurden aufgezeichnet und in Testskripte übersetzt, die die Tester abwandelten, um aus einer Anfrage viele Varianten für einen umfassenden Funktionstest zu machen. Die Skripte waren in Visual Basic for Applications geschrieben, also für jeden zugänglich, der VB kennt. Mit ihnen ließen sich auch die Antworten gegen erwartete Ergebnisse prüfen. Unerwartete Ergebnisse wurden aussortiert und an das Fehlermeldesystem übergeben.

Andere Testfirmen wie Software Research, Inc., Logica und Compuware arbeiteten damals an ähnlichen Ansätzen. Absehbar war, dass der Markt bald voll von Werkzeugen für den Webservice-Test sein würde und dass es danach noch dauern würde, bis sie die gewünschte Qualität erreichen. Bis dahin sahen Sneed und Huang Raum für maßgeschneiderte Lösungen wie die hier beschriebene. Die verschiedenen Ansätze der Testautomatisierung behandeln Graham und Fewster.

Der WSDLTest-Ansatz

WSDLTest geht einen etwas anderen Weg als die kommerziellen Werkzeuge. Es setzt beim Schema der WSDL-Beschreibung an und beginnt mit einer statischen Analyse. Aus dem Schema entstehen zwei Objekte: eine Vorlage für eine Dienstanfrage und ein Testskript. Mit dem Testskript verändert der Anwender die Argumente in der Anfragevorlage und prüft die Ergebnisse in der Antwort des Webservice. Der Testtreiber ist ein eigenes Werkzeug, das die Anfragen liest und verschickt und die Antworten empfängt und speichert.

Warum das Werkzeug entstand

Den Anlass gab ein Projekt: Getestet werden sollte eine E-Government-Website. Der Bürger griff über die normale Web-Oberfläche zu. Die Kommunalbehörden hatten aber IT-Systeme, die ebenfalls auf die Website zugreifen mussten, um Informationen aus der zentralen staatlichen Datenbank abzurufen. Dafür bekamen sie eine Webservice-Schnittstelle mit neun Diensten, jeder mit eigenen Anfrage- und Antwortformaten.

Die Oberfläche der E-Government-Website testeten menschliche Tester von Hand, indem sie das Verhalten möglicher Benutzer nachspielten. Für die Webservices brauchte es ein Werkzeug, das die Benutzerprogramme simuliert, also automatisch typische Anfragen erzeugt und an den Webservice schickt. Weil die Antworten des Dienstes nicht ohne Weiteres sichtbar sind, mussten auch sie automatisch validiert werden. Die Motivation lässt sich so zusammenfassen:

  • Webservices sind nicht von vornherein vertrauenswürdig und müssen deshalb intensiv getestet werden.
  • Alle Anfragen sollten mit allen repräsentativen Kombinationen von Argumenten getestet werden.
  • Alle Antworten sollten mit allen repräsentativen Ergebniszuständen validiert werden.
  • Um die Zielfunktionen zu prüfen, braucht es WSDL-Anfragen mit gezielt gewählten Argumenten.
  • Um die Korrektheit zu prüfen, müssen die WSDL-Antworten gegen die erwarteten Ergebnisse validiert werden.

Eine Anfragevorlage aus dem WSDL-Schema erzeugen

Jeder Test ist ein Test gegen etwas. Es braucht eine Quelle für die Testdaten und ein Orakel, mit dem sich die Ergebnisse vergleichen lassen. Bei WSDLTest ist das Orakel das WSDL-Schema. Es wird entweder automatisch aus dem Schnittstellenentwurf erzeugt oder vom Entwickler von Hand geschrieben. Als dritte Möglichkeit lässt es sich aus der BPEL-Prozessbeschreibung ableiten.

Wie auch immer es entsteht, das Schema definiert die komplexen Datentypen nach den Regeln des XML-Schema-Standards. Komplexe Typen können andere komplexe Typen enthalten, sodass ein Datenbaum mit einfach und mehrfach vorkommenden Knoten entsteht. Danach legt das Schema die Basisknoten fest, also die Endpunkte des Baums, und ihre Reihenfolge. Das sind die eigentlichen Parameter. Ein Auszug aus den Typdefinitionen eines E-Government-Webservice:

<definitions>
  <types>
    <schema>
      <complexType name="getProfile">
        <sequence>
          <element name="DatWsRequest_1"/>
        </sequence>
      </complexType>
      <complexType name="DatWsRequest">
        <sequence>
          <element name="memoID" type="string" nillable="true"/>
        </sequence>
      </complexType>
    </schema>
  </types>
  
  <message name="DatWebService_getProfile">
    <part name="parameters" element="getProfile"/>
  </message>

  <portType name="DatWebServiceInterface">
    <operation name="getProfile">
      <input message="Interface_getProfile"/>
      <output message="getProfileResponse"/>
      <fault name="DatWsException" message="DatWsException"/>
    </operation>
  </portType>
  
  <binding name="DatWebServiceInterfaceBinding" type="tns:DatWebServiceInterface">
    <soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document"/>
  </binding>
</definitions>

Nach den Parametern folgen die Nachrichtenbeschreibungen. Sie benennen jede Nachricht und ihre Bestandteile, egal ob Anfrage oder Antwort. Dann kommen die Porttypen: Jede aufrufbare Dienstoperation steht dort mit den Namen ihrer Eingangs- und Ausgangsnachrichten. Diese Namen verweisen auf die zuvor definierten Nachrichten, die ihrerseits auf die Parameter verweisen. Nach den Porttypen folgen die Bindings, die die aus Dienstoperationen zusammengesetzten SOAP-Prototypen beschreiben.

Ein WSDL-Schema ist also ein Baum. Die SOAP-Prototypen verweisen auf Dienstoperationen, diese auf logische Nachrichten, diese auf Parameter und diese auf Datentypen, die sich wiederum gegenseitig referenzieren können. Das Parsen dieses Baums heißt Tree Walking. Der Parser wählt einen obersten Knoten und folgt ihm durch alle Zweige, wobei er auf dem Weg nach unten alle untergeordneten Knoten einsammelt. Am Ende jedes Zweigs stehen die Grunddatentypen wie Integer, Boolean und String.

WSDLTest geht einen Schritt weiter und weist jedem Grunddatentyp einen Satz repräsentativer Werte zu, etwa Integer-Werten einen Bereich von 0 bis 10000 und String-Werten verschiedene Zeichenkombinationen. Diese Wertemengen liegen in Tabellen, die der Anwender vor der Testdatengenerierung bearbeiten kann. Die vollständige Struktur einer WSDL-Schnittstelle sieht damit so aus:

Web service interface
SOAP prototypes
service operations
logical messages
parameters
data types
elementary data types
representative values

Der Datengenerator durchläuft den Schemabaum bis zu den Basisdatentypen und wählt für jeden Typ einen repräsentativen Wert, zufällig aus der Menge der möglichen Werte. Daraus entsteht eine XML-Datengruppe:

<validate>
  <validate>
    <DatWsRequest>
      <memoID>Kat221</memoID>
      <subCompID>ThisData</subCompID>
      <functionID>4711</functionID>
      <version>1</version>
    </DatWsRequest>
  </validate>
</validate>

So entsteht eine WSDL-Anfragedatei mit Beispieldaten, die für spätere Läufe gespeichert wird. Zugleich erzeugt das Werkzeug ein Testskript, mit dem der Tester die generierten Werte überschreiben kann: eine Vorlage mit den Namen der Datenelemente und ihren Werten. Ändert der Tester das Skript, ändert er die Werte der Dienstanfrage. Weil auch für die Ausgabedaten ein Skript entsteht, bekommt er zugleich eine Vorlage, um die Antworten zu prüfen.

<complexType name="getProfile">
  <complexType name="DatWsRequest">
    <sequence>
      <element name="memoID" type="string" nillable="true"/>
      <element name="subCompID" type="string" nillable="true"/>
      <element name="functionID" type="string" nillable="true"/>
      <element name="version" type="string" nillable="true"/>
    </sequence>
  </complexType>
</complexType>
<getProfile>
  <getProfile>
    <DatWsRequest>
      <memoID>XXXXXXXXX</memoID>
      <subCompID>YYYYYYYYY</subCompID>
      <functionID>ZZZZZZZZZ</functionID>
      <version>ThisData</version>
    </DatWsRequest>
  </getProfile>
</getProfile>

Das ist das Ergebnis der Schemaanalyse, eine WSDL-Vorlage als Eingabe für die eigentliche Testdatengenerierung. Eine WSDL-Anfrage ist eine kaskadierende Struktur. Sie beginnt mit Typdefinitionen, auf die sich die Nachrichtendefinitionen beziehen, auf die sich wiederum die Ein- und Ausgabeoperationen eines Ports beziehen. Der größte Teil des Codes, etwa für Ports und SOAP-Container, ist rein technisch und wiederholt sich stark. Deshalb ist eine Vorlage praktisch, die man kopiert und an die jeweilige Anfrage anpasst.

Vorbedingungen als Assertions schreiben

Die Testskripte für WSDLTest sind Folgen von Vorbedingungen (Pre-Condition-Assertions), die mögliche Zustände einer Webservice-Anfrage festlegen. Dieselbe Assertion-Sprache dient auch zum Testen anderer Datenbestände wie relationaler Datenbanken, XML-Dateien und Textdateien. Ein Zustand ist eine Kombination vorgegebener Werte für die Datentypen der WSDL-Schnittstelle. Die Werte gehören zu einzelnen Datenelementen, ihre Zuweisung kann aber voneinander abhängen, sodass etwa der Tester eine bestimmte Wertekombination festlegt:

assert new.Account_Status = "y"
if(old.Account_Balance < "0");

Für die Zuweisung von Testdaten gibt es sechs Assertion-Typen:

  • Verweis auf einen anderen vorhandenen Datenwert derselben Schnittstelle
  • konstanter Wert
  • Menge von Alternativwerten
  • Wertebereich
  • verketteter Wert
  • berechneter Wert

Ein Verweis zeigt auf einen anderen vorhandenen Wert. Dieser Wert muss in derselben WSDL liegen.

assert new.Account_Owner = old.Customer_Name;

Eine Konstante steht als Literal in der Anweisung. Alle Literale stehen in Anführungszeichen.

assert new.Account_Balance = "0";

Alternativwerte werden aufgezählt und durch das Oder-Zeichen ”!” getrennt.

assert new.Account_Status = "0" ! "1" ! "2" ! "3";

Die Werte werden reihum zugewiesen, beginnend mit 0. Das erste Konto bekommt den Status 0, das zweite 1, das dritte 2 usw.

Ein Wertebereich dient der Grenzwertanalyse. Er wird über die untere und die obere Grenze eines numerischen Bereichs angegeben.

assert new.Account_Status = ["1" : "5"];

Das ergibt reihum die Werte 0, 1, 2, 4, 5, 6. Das erste Konto bekommt den Status 0, das zweite 1, das dritte 2, das vierte 4, das fünfte 5 und das sechste 6.

Ein verketteter Wert verbindet zwei oder mehr vorhandene Werte mit zwei oder mehr Konstanten zu einem einzigen String.

assert new.Account_Owner =  
"Mr. " | Customer_Name | " from " | Customer_City;

Ein berechneter Wert ist ein arithmetischer Ausdruck, dessen Argumente vorhandene Werte oder Konstanten sein können.

assert new.Account_Balance = old.Account_Balance / "2" + "1";

Zuweisungen können bedingt oder unbedingt sein. Eine bedingte Zuweisung trägt einen logischen Ausdruck, der eine Datenvariable der WSDL-Schnittstelle mit einer anderen Variablen derselben Schnittstelle oder mit einer Konstante vergleicht.

assert new.Account_Owner = "Smith"  
if(old.Account_Number = "100922" &  
old.Account_Balance > "1000");

Der Tester passt die Assertions im generierten Skript so an, dass ein repräsentatives Testdatenprofil entsteht, mit Äquivalenzklassen und Grenzwertanalyse, mit steigenden und fallenden Wertefolgen. Ziel ist, die Eingabedaten so zu variieren, dass ein breites Spektrum repräsentativer Anfragen getestet wird. Dazu muss der Tester wissen, was der Webservice tun soll, und die Werte entsprechend zuweisen.

file: DAT-WS;  
if (object = "DatWsRequest");  
assert new.memoID = old.memoID;  
assert new.subCompID = old.subCompID;  
assert new.functionID = "4711";  
assert new.version = "1";  
endObject;  
if (object = "DatWSProfile");  
assert new.attributeName = old.attributeName;  
assert new.typ = 21 ! 22 ! 23;  
assert new.value = "Sneed";  
endObject;  
if (object = "DatWsCheckAssertion");  
assert new.memoID = "Kati";  
assert new.version = "3";  
assert new.notAfter = "ThisData";  
assert new.notBefore = "ThatData";  
endObject;  
end;

Die Vorlagedaten überschreiben

Liegen die Assertion-Skripte vor, lässt sich die Vorlage einer Webservice-Anfrage mit den zugesicherten Daten überschreiben. Das übernimmt das Modul XMLGen. Es gleicht die vom Modul WSDLGen erzeugte WSDL-Datei mit dem Assertion-Skript des Testers ab. Die Datennamen im Skript werden gegen das WSDL-Schema geprüft und die Assertions in Symboltabellen kompiliert, getrennt nach Konstanten, Variablen, Zuweisungen und Bedingungen.

Danach liest XMLGen die WSDL-Datei und ersetzt die Datenwerte durch die Werte aus den Assertions. Verweist eine Assertion auf eine Konstante, ersetzt diese den vorhandenen Wert des gleichnamigen XML-Elements. Verweist sie auf eine Variable, wandert der Wert des XML-Elements mit diesem Variablennamen in das Zielelement. Alternativwerte werden der Reihe nach zugewiesen, bis der letzte erreicht ist, dann beginnt es wieder beim ersten. Bereichswerte werden als Grenzwerte plus und minus eins zugewiesen.

<DatWsCheckAssertion>
  <memoID>Kati</memoID>
  <version>3</version>
  <DatWsConditions>
    <notAfter>ThisData</notAfter>
    <notBefore>ThatData</notBefore>
  </DatWsConditions>
</DatWsCheckAssertion>

Am Ende steht eine Folge von Webservice-Anfragen mit unterschiedlichen repräsentativen Zuständen, die die ursprünglich generierten Daten mit den zugewiesenen kombiniert. Über die Assertions verändert der Tester die Zustände der Anfragen und sorgt so für maximale Datenabdeckung.

Die Webservices aufrufen

Die erzeugten Anfragen gehen über den WS-Testtreiber an den Server. Der Treiber ist ein simulierter BPEL-Prozess mit einer Schleife, die über eine Liste von Webservices in der Reihenfolge ihres Aufrufs gesteuert wird. Der Tester kann die Liste bearbeiten und die Reihenfolge an die Testanforderungen anpassen.

Aus der Liste nimmt der Treiber den Namen des nächsten Webservice, liest die generierte WSDL-Datei dieses Namens und schickt eine Reihe von Anfragen an den Dienst. Im synchronen Modus wartet er auf jede Antwort, bevor er die nächste Anfrage schickt. Im asynchronen Modus schickt er mehrere Anfragen, bis in der Liste ein Wartebefehl steht. Dann wartet er, bis alle versendeten Anfragen beantwortet sind, und macht erst danach weiter.

Die Antworten landen in eigenen Antwortdateien und werden später von einem Postprozessor geprüft. Der Treiber erzeugt keine Anfragen und prüft keine Antworten. Die Anfragen erstellt der Präprozessor, der Treiber verschickt sie nur. Die Antworten prüft der Postprozessor, der Treiber speichert sie nur. Damit schrumpft der Testtreiber auf die Rolle eines einfachen Dispatchers. BPEL-Prozeduren eignen sich dafür gut, weil sie alles mitbringen, um Webservices in einem vordefinierten Workflow aufzurufen.

Nachbedingungen als Assertions schreiben

Für die Prüfung der Antworten nutzt WSDLTest dieselbe Assertion-Sprache wie für den Bau der Anfragen, nur mit umgekehrter Bedeutung. Hier weisen die Assertions keine Werte zu. Sie vergleichen die Antwortdaten mit den Werten vorheriger Antworten oder mit Konstanten.

Ein Vergleich mit einem anderen vorhandenen Wert verweist auf diesen Wert.

assert new.Account_Owner = old.Account_Owner;

Ein Vergleich mit einer Konstante gibt den Wert als Literal an, wie bei der Zuweisung in Anführungszeichen.

assert new.Account_Balance = "33.50";

Ob die Antwortdaten mindestens einem Wert aus einer Liste entsprechen, prüft die Alternativ-Assertion.

assert new.Account_Status = "0" ! "1" ! "2" ! "3" ;

Eine Assertion kann auch prüfen, ob die Antwortdaten in einem Wertebereich liegen, etwa zwischen 100 und 500.

assert new.Account_Balance = ["100.00" : "500.00"];

Auch berechnete Werte sind möglich. Zuerst wird der Ausdruck ausgewertet, dann werden die Antwortdaten geprüft.

assert new.Account_Balance = old.Account_Balance - "50";

Wie die Vorbedingungen können auch die Nachbedingungen unbedingt oder bedingt sein. Bedingte Nachbedingungen schränkt ein logischer if-Ausdruck ein. Er vergleicht zwei Variablen der Webservice-Antwort oder eine Variable der Antwort mit einer Konstante.

assert new.Account_Status = "3"  
if(old.Account_Balance < "1000");

Ist eine Assertion nicht erfüllt, protokolliert das Werkzeug eine Fehlermeldung mit erwartetem und tatsächlichem Wert. Gibt es für jedes Attribut der WSDL-Antwort eine Assertion, ist die Antwort zu 100 Prozent geprüft. Nötig ist das nicht. Der Tester kann sich auf kritische Variablen beschränken, dann sinkt die Datenabdeckung. Gemessen wird sie als Verhältnis der geprüften Ergebnisse zur Summe aller Ergebnisse.

Die Antworten validieren

Die Ergebnisse des Webservice prüft das Modul XMLVal. Zuerst übersetzt es die Nachbedingungen in interne Tabellen mit Variablenverweisen, Konstanten, Aufzählungen und Bereichen. Dabei prüft es Datennamen und Datentypen gegen die Deklarationen im WSDL-Schema, damit beides konsistent ist.

+-----------------------------------------------------------+
| WSDL Response Validation Report                           |
| File: DAT-WS.wsdl Params: Y Y Y Y                         |
| Object: DatWsCheckAssertion Date: 26.02.06                |
| Type : WSDL System: WebService                            |
|                                                           |
| Key Fields of Record(new,old)                             |
+-----------------------------------------------------------+
| New:DatWsCheckAssertion                                   |
| Old:DatWsCheckAssertion                                   |
+-----------------------------------------------------------+
| Non-Matching Fields    | Non-Matching Values              |
+------------------------+----------------------------------+
| RecKey:27013           |                                  |
| New: memoId            | Marta                            |
| Old: memoId            | Kati                             |
+------------------------+----------------------------------+
| RecKey:27022           |                                  |
| New: Version           | 2                                |
| Old: Version           | 1                                |
+-----------------------------------------------------------+
| Total Number of old Responses checked: 10                 |
| Number of old Responses found in new File: 10             |
| Number of old Responses not in new File: 00               |
| Number of new Responses found in old File: 10             |
| Number of new Responses not in old File: 00               |
| Total Number of Attributes checked: 70                    |
| Total Number of non-Matching Attributes: 07               |
| Percentage of matching Attributes: 90 %                   |
| Percentage of matching Responses: 100 %                   |
+-----------------------------------------------------------+

Mit den kompilierten Tabellen prüft das Werkzeug dann die Antwort des Webservice. Erstens speichert es die erwarteten Werte in einer Tabelle mit einem Schlüssel je Objektvorkommen. Zweitens parst es die WSDL-Ergebnisdatei und gleicht die Objekte dort mit denen in den Assertion-Tabellen ab. Bei einem Treffer extrahiert es die Attribute des Objekts und vergleicht ihre Werte mit den erwarteten. Erfüllen sie die Prüfbedingung nicht, landen Datenname und Wert in einer Liste der Abweichungen. Warum die Ergebnisse abweichen, muss der Tester herausfinden. Neben dieser Liste erstellt XMLVal Statistiken zum Grad der Datenabdeckung und zum Grad der Korrektheit.

Die Bausteine von WSDLTest

WSDLTest wurde aus mehreren Komponenten zusammengesetzt:

  • GUI Shell
  • XML File Writer
  • XML File Reader
  • Assertion Compiler
  • XSD Tree Walker
  • WSDL Analyzer
  • Table Processor
  • Error Handler
  • Random Request Data Generator
  • Selective Request Generator
  • Request Validator
  • Validation Report Generator
  • WS Test Driver

Einige dieser Komponenten stammen aus einem bestehenden Werkzeug, andere wurden neu entwickelt. Das Konzept der XML-Verarbeitung ist veröffentlicht und kann wiederverwendet werden. Assertion-Sprache und Assertion-Compiler kamen aus einem früheren Werkzeug und wurden erweitert. So war eine Prototypversion in weniger als einem Monat einsatzbereit. Später wurde das Werkzeug verfeinert und ausgebaut. In seiner ursprünglichen Form war es so aufgebaut:

  • Die Shell ist in Borland Delphi implementiert.
  • Der Kern ist in Borland C++ implementiert.
  • Das Werkzeug nutzt keine Datenbank, nur temporäre Arbeitsdateien.
  • Es wurde für MS Windows oder eine vergleichbare Umgebung entwickelt.
  • Shell und Kern sind über eine XML-Parameterdatei verbunden.

Die Benutzeroberfläche

Die Windows-Oberfläche von WSDLTest nimmt Parameter vom Anwender entgegen, wählt Dateien aus einem Verzeichnis aus und ruft die Backend-Prozesse auf. Dateien lassen sich aus drei Verzeichnissen wählen:

  • Assertion-Verzeichnis mit den Assertion-Textdateien
  • altes Dateiverzeichnis mit den Testeingaben (csv-, sql-, xml- und wsdl-Dateien)
  • neues Dateiverzeichnis mit den Testausgaben (csv-, xml- und wsdl-Dateien)

Dazu kommt ein Ausgabeverzeichnis für Protokolle und Berichte. Die Dateinamen müssen in allen vier Verzeichnissen gleich sein, denn darüber werden die Dateien verknüpft. Nur die Endung darf abweichen. So wird die tatsächliche Antwort “Message.wsdl” mit der erwarteten Antwort “Message.wsdl” über das Assertion-Skript “Message.asr” verglichen, daraus entsteht der Bericht “Message.rep”. Assertion-Skripte haben die Endung .asr. Die Namen aller Dateien eines Projekts werden mit ihren Typen angezeigt, damit der Anwender sie auswählen kann.

Der Assertion-Compiler

Der Assertion-Compiler liest die Assertion-Skripte und übersetzt sie in interne Tabellen, die zur Testzeit interpretiert werden. Je Datei bzw. Objekt entstehen neun Tabellen:

  • eine Kopftabelle mit Informationen über das Prüfobjekt
  • eine Schlüsseltabelle mit bis zu 10 Schlüsseln, über die alte und neue Dateien in Beziehung gesetzt werden
  • eine Assertion-Tabelle mit bis zu 80 Assertion-Anweisungen
  • eine Bedingungstabelle mit einem Eintrag je zu erfüllender Vorbedingung
  • eine Konstantentabelle mit einem Eintrag je zu vergleichendem oder zu erzeugendem Konstantenwert
  • eine Alternativentabelle mit einem Eintrag je möglichem Alternativwert eines Attributs
  • eine Verkettungstabelle mit einem Eintrag je verkettetem Wert
  • eine Rechentabelle mit den Operanden und Operatoren der arithmetischen Ausdrücke
  • eine Ersetzungstabelle mit bis zu 20 Feldern, deren Werte durch andere Werte ersetzt werden können

Kompiliert wird, bevor eine Datei generiert oder validiert werden kann. Viele Assertion-Skripte lassen sich auf einmal kompilieren, bevor Generierung und Validierung beginnen. Die Ergebnisse schreibt der Compiler in eine Protokolldatei, die der Anwender am Ende jedes Laufs zu sehen bekommt.

Der WSDL-Request-Generator

Der Anfragegenerator besteht aus drei Komponenten:

  • Schema-Präprozessor
  • Zufallsgenerator für Anfragen
  • Datenzuweiser

XML-Schemas werden meist von einem Werkzeug erzeugt, das Tags mit vielen Attributen zusammen in eine Zeile schreibt. Der Schema-Präprozessor teilt solche langen Zeilen in viele eingerückte kurze Zeilen auf. Das macht den XML-Text lesbarer und leichter zu parsen und vereinfacht die Arbeit der nachfolgenden Komponenten.

Der Zufallsgenerator erzeugt dann eine Reihe von WSDL-Anfragen mit konstanten Werten als Daten. Welche Konstante ein Element bekommt, hängt von seinem Typ ab. String-Daten kommen aus repräsentativen Strings, numerische Daten entstehen durch Addieren oder Subtrahieren konstanter Intervalle, Datums- und Zeitwerte stammen aus der Uhr. In einer Anfrage können Daten als Variable zwischen den Tags eines Elements oder als Attribut eines Elements stehen. Attribute haben allerdings keinen Typ, sie sind per Definition Strings, deshalb bekommen sie nur String-Werte. Heraus kommt eine Anfrage im XML-Format mit zufällig belegten Elementen und Attributen.

Der Datenzuweiser liest die zufällig erzeugten WSDL-Anfragen ein und schreibt sie mit angepassten Datenvariablen wieder aus. In den Assertion-Skripten ordnet der Anwender den Elementen der Anfrage namentlich bestimmte Argumente zu. Beim Kompilieren landen Namen und Argumente in Symboltabellen. Für jedes Datenelement prüft der Datenzuweiser dort, ob ihm ein Assertion-Wert oder eine Wertemenge zugewiesen wurde. Wenn ja, überschreibt der zugesicherte Wert den Zufallswert, sonst bleibt der Zufallswert stehen. So werden die WSDL-Anfragen zu einer Mischung aus Zufallswerten, repräsentativen Werten und Grenzwerten, und der Tester steuert über die Assertions, welche Argumente beim Webservice ankommen. Die Anfragen liegen in einer temporären Datei für den Request-Dispatcher bereit. Damit sie unterscheidbar sind, bekommt jede eine Testfallkennung als eindeutigen Schlüssel.

Der WSDL-Antwort-Validator

Der Antwort-Validator besteht aus nur zwei Komponenten:

  • Antwort-Präprozessor
  • Daten-Checker

Der Antwort-Präprozessor arbeitet ähnlich wie der Schema-Präprozessor. Er entwirrt die Antworten, die vom Webservice zurückkommen, damit der Daten-Checker sie leichter verarbeiten kann. Die Antworten holt er aus der Warteschlangendatei des Test-Dispatchers.

Der Daten-Checker liest die Antworten und erkennt die Ergebnisse an ihrem Tag oder ihrem Attributnamen. Jeden Namen gleicht er mit den Datennamen in der Assertion-Tabelle für die Ausgabe ab. Bei einem Treffer vergleicht er den Wert des Elements oder Attributs mit dem zugesicherten Wert. Weicht der tatsächliche Wert ab, meldet er die Diskrepanz im Validierungsbericht. Das wiederholt er für jede Antwort.

Die Testfallkennung der Anfrage wird an die Antwort vererbt, sodass jede Antwort mit ihrer Anfrage verknüpft ist. Der Tester kann die Kennung deshalb in die Assertions aufnehmen und Antworten je Testfall vergleichen.

Der WSDL-Request-Dispatcher

Der Request-Dispatcher ist die einzige Java-Komponente. Er nimmt die generierten Anfragen aus der Eingabe-Warteschlange, packt sie in einen SOAP-Umschlag und schickt sie an den gewünschten Webservice. Gibt es ein Problem beim Erreichen des Webservice, behandelt er die Ausnahme. Andernfalls zeichnet er Ausnahmen nur auf und macht mit der nächsten Anfrage weiter. Die Antworten nimmt er aus dem SOAP-Umschlag und legt sie in der Ausgabe-Warteschlange ab, wo die Testfallkennung sie eindeutig identifiziert.

Außerdem zeichnet der Dispatcher auf, wann jeder Testfall verschickt wird und wann die Antwort zurückkommt. Das sind Start- und Endzeit des Testfalls. Werden die Methoden der Webservices mit Sonden instrumentiert, die den Zeitpunkt ihrer Ausführung festhalten, lassen sich bestimmte Methoden bestimmten Testfällen zuordnen. Das erleichtert die Fehlersuche in den Webservices. Es zeigt auch, welche Methoden eine Anfrage auf ihrem Weg durch einen oder mehrere Webservices durchläuft. Sneed und Huang sahen darin ein Thema für weitere Forschung.

Was hat der Einsatz im E-Government-Projekt gezeigt?

Die Erfahrungen mit WSDLTest im E-Government-Projekt waren ermutigend. Das Werkzeug war noch in Entwicklung, aber bereits im Einsatz. Getestet wurden neun Webservices mit durchschnittlich 22 Anfragen je Dienst. Von 47 verifizierten Antworten enthielten 19 mindestens ein fehlerhaftes Ergebnis. Von den mehr als 450 Fehlern im Gesamtprojekt fand der Webservice-Test etwa 23.

Wie viele davon ein anderes Testvorgehen gefunden hätte, lässt sich schwer sagen. Um die Effizienz eines Ansatzes zu beurteilen, müsste man ihn mit einem anderen vergleichen. Fest steht, dass der Test relativ günstig war und die Tester nicht mehr als zwei Wochen Aufwand gekostet hat.

Das Werkzeug eignet sich für den Test von Webservices, solange die WSDL-Schnittstellen nicht zu komplex sind. Sind sie es, wird das Schreiben der Assertions zu schwierig, und es schleichen sich Fehler ein. Dann kann der Tester nicht mehr sicher sagen, ob ein beobachteter Fehler vom Webservice kommt oder von einer falsch formulierten Assertion.

Ähnliches berichten Sneed und Huang aus dem US-amerikanischen Raketenabwehrprojekt etwa 30 Jahre zuvor. Dort waren rund 40 Prozent der gemeldeten Fehler in Wahrheit Fehler in den Testverfahren. Die Testtechnik, die das RXVP-Testlabor in diesem Projekt einsetzte, zeigt, wie wenig sich die grundlegenden Methoden seit den 70er Jahren verändert haben: Vorbedingungen setzen, Nachbedingungen prüfen, Software instrumentieren, Testpfade überwachen, Testabdeckung messen. Geändert hat sich nur die Umgebung.

Offene Aufgaben für Testwerkzeuge

Softwaretest ist eine komplexe Aufgabe, die noch nicht vollständig verstanden ist. Sie berührt viele Themen, darunter:

  • Testfälle spezifizieren
  • Testdaten erzeugen
  • Testdurchführung überwachen
  • Testabdeckung messen
  • Testergebnisse validieren
  • Systemfehler verfolgen, etc.

WSDLTest deckt davon nur zwei ab, die Erzeugung von Testdaten und die Validierung der Testergebnisse. Ein weiteres Werkzeug, TextAnalyzer, analysiert Anforderungsdokumente und extrahiert daraus funktionale und nichtfunktionale Testfälle, die in einer Testfalldatenbank landen. Der nächste Schritt wäre, aus diesen abstrakten Testfällen die Vor- und Nachbedingungen zu erzeugen und so die Lücke zwischen Anforderungsspezifikation und Testspezifikation zu schließen. Das größte Hindernis ist die Informalität der Anforderungen. Aus einer abstrakten, informellen Beschreibung müssen formale, detaillierte Ausdrücke werden. Vor demselben Problem steht die modellgetriebene Entwicklung.

Eine zweite Richtung ist die Testüberwachung, also den Weg einer Webservice-Anfrage durch das System zu verfolgen. Dafür gibt es TestDocu, ebenfalls von Sneed und Huang. Es instrumentiert die Serverkomponenten, sodass sie aufzeichnen, an welcher Anfrage sie gerade arbeiten. Über die Trace-Datei lässt sich die Ausführungsreihenfolge der Webservices überwachen, denn an einer Anfrage sind oft viele Dienste beteiligt. Die größte Aufgabe bleibt, diese Werkzeuge so zu integrieren, dass sie sich als Ganzes nutzen lassen. Das heißt, ein generisches Test-Framework aufzubauen, mit gemeinsamer Ontologie und standardisierten Schnittstellen zwischen den Werkzeugen.

Offen bleibt die Grundsatzfrage, wie weit der Test automatisiert werden sollte. Vielleicht ist es klüger, den Web-Testprozess nicht vollständig zu automatisieren und stattdessen auf die Fähigkeiten und die Kreativität menschlicher Tester zu setzen, denn Automatisierung neigt dazu, wichtige Probleme zu verdecken. Testautomatisierung oder kreatives Testen bleibt ein wichtiges Thema der Testliteratur.

Häufig gestellte Fragen

Wie testet man einen Webservice?

Indem man seine Nutzung simuliert: Anfragen automatisch erzeugen, direkt an den Dienst schicken und die Antworten automatisch gegen erwartete Werte prüfen. Der Weg über die Benutzeroberfläche gilt dafür als wenig wirksam. Getestet wird unabhängig und in der Zielumgebung, jeder Dienst einzeln und im Zusammenspiel, mit repräsentativen Datenkombinationen für eine hohe funktionale Abdeckung.

Wie testet man eine WSDL-Schnittstelle?

Ausgangspunkt ist das WSDL-Schema. Ein Parser durchläuft den Baum von den SOAP-Prototypen über Operationen, Nachrichten und Parameter bis zu den Grunddatentypen und belegt jeden Typ mit repräsentativen Werten. Daraus entstehen eine Anfragevorlage und ein Skript für die Prüfung der Antworten. Zugleich dient das Schema als Orakel, gegen das Datennamen und Datentypen geprüft werden.

Wie werden SOAP-Nachrichten beim Webservice-Test verschickt und ausgewertet?

Ein Dispatcher packt jede generierte Anfrage in einen SOAP-Umschlag, schickt sie an den Dienst und legt die Antwort ohne Umschlag in einer Ausgabedatei ab. Eine Testfallkennung verknüpft Anfrage und Antwort. Ein Validator vergleicht die Antwortwerte danach mit den Nachbedingungen und protokolliert jede Abweichung mit erwartetem und tatsächlichem Wert.

Diese Seite teilen

Ähnliche Beiträge