Datengetriebenes Testen ist ein sehr wichtiger Aspekt der Testautomatisierung. Das Ziel
besteht, kurz gesagt, darin, einen Testfall mehrfach mit dem selben Ablauf, aber
unterschiedlichen Eingabe- und Vergleichswerten durchzuführen. QF-Test bietet verschiedene
Möglichkeiten, um Daten für datengetriebene Tests abzulegen oder aus externen Quellen zu
laden. Die bequemste Variante basiert auf einem 'Datentreiber' Knoten, der die Umgebung zur
Iteration über die Datensätze bereitstellt, sowie ein oder mehrere 'Daten' Knoten,
welche die Variablen Werte für die Testdurchläufe liefern. Dabei gibt es in QF-Test keinen
'Daten' Knoten als solches. Er dient als Oberbegriff für spezielle Ausprägungen wie
eine 'Datentabelle' oder eine 'CSV Datei'. Das Ganze lässt sich am besten
anhand von Beispielen erläutern. Eine Demo-Testsuite mit einfachen und komplexeren
Beispielen finden Sie unter dem Namen datadriver.qft
im Verzeichnis
doc/tutorial
unterhalb des Wurzelverzeichnises von QF-Test.
Bitte beachten Sie, dass Sie veränderte Testsuiten am besten in einem projektspezifischen Ordner speichern.
Obige Abbildung zeigt einen 'Testfallsatz' mit einem 'Datentreiber' Knoten, der einen
einzelnen 'Daten' Knoten in Form einer 'Datentabelle' enthält. Der Inhalt
des 'Datentabelle' Knotens ist wie folgt:
Wird der 'Testfallsatz' ausgeführt, iteriert er über die Zeilen der 'Datentabelle'.
Für jeden der drei Iterationsschritte werden die Werte der entsprechenden Zeile der
Tabelle an die Variablen mit dem Namen der jeweiligen Spalte gebunden. Im Beispiel ergibt
das für den ersten Durchlauf die Bindungen "Model=Rolo", "Variant=None" und "Price=19000".
Im zweiten Durchlauf ist dann "Model=I5", im dritten "Model=Minigolf". Bei jedem Durchgang
werden alle 'Testfall' Childknoten des 'Testfallsatz' Knotens ausgeführt.
Die folgende Abbildung zeigt ein Protokoll für obigen 'Testfallsatz':
Im nächsten Beispiel sehen wir, dass datengetriebene Tests nicht auf eine einfache
Schleife beschränkt sind:
Der 'Datentreiber' enthält nun eine zweite 'Datentabelle' mit folgendem Inhalt:
Der 'Testfallsatz' wird nun insgesamt sechs Iterationen durchlaufen, da für jede der drei
Iterationen der äußeren Schleife namens "cars" beide Iterationen der inneren Schleife
namens "accessories" durchlaufen werden. Im folgenden Protokoll ist dies gut zu erkennen:
Hinweis Die äußerst hilfreichen dynamisch generierten Namen der
Schleifendurchgänge erhalten Sie, indem Sie das Attribut 'Name für Schleifendurchgang im Protokoll' des
'Datentreiber' Knotens auf den Wert "car Model: $(Model)" im ersten bzw. "car Model:
$(Model), accessory Name: $(Accessory)" im zweiten Beispiel setzen. Wie Sie sehen, wird dieser
Wert für jede Iteration individuell expandiert, so dass Sie auf die Variablen zugreifen
können, die für die Iteration gebunden werden.
Wie im vorangehenden Beispiel erklärt, muss ein 'Datentreiber' Knoten in einen
'Testfallsatz' Knoten eingefügt werden, und zwar zwischen die optionalen 'Abhängigkeit' und
'Vorbereitung' Knoten. Wird der 'Testfallsatz' ausgeführt, führt er zunächst einen
eventuell vorhandenen 'Datentreiber' Knoten aus. Der Inhalt des 'Datentreiber' Knotens
ist nicht auf 'Datentabelle' und 'CSV Datei' beschränkt. Wie ein normaler
'Sequenz' Knoten kann der 'Datentreiber' jede Art von ausführbaren Knoten
enthalten, um damit eventuelle Vorbereitungen durchzuführen, die zur Bereitstellung der
Daten notwendig sind. Hierdurch ist es auch möglich, die 'Daten' Knoten mehrfach
zu nutzen, indem diese einfach in eine 'Prozedur' gestellt werden, welche aus den
'Datentreiber' Knoten heraus aufgerufen wird.
Im Prinzip entspricht ein 'Daten' Knoten einer Schleife, bei der in jedem Durchgang
verschiedene Werte an Variablen gebunden werden. Ein 'Daten' Knoten muss mit einem
Namen im 'Datentreiber' Kontext eines 'Testfallsatzes' registriert werden. Dadurch kann die
Schleife durch einen 'Break' Knoten mit dem gleichen Namen abgebrochen werden.
Nachdem der 'Datentreiber' ausgeführt wurde, iteriert der 'Testfallsatz' über die dabei
registrierten 'Daten' Schleifen.
Bei verschachtelten Schleifen wird der zuerst registrierte 'Daten' Knoten als
äußerste Schleife ausgeführt. Seine Variablen werden zuerst gebunden und haben damit
geringere Bindungskraft als die Variablen der inneren Schleife(n).
In der mitgelieferten Testsuite
doc/tutorial/datadriver.qft
finden Sie Beispiele für die
Verwendung von CSV und Exceldateien.
Neben dem 'Datentabelle' Knoten gibt es verschiedene weitere Möglichkeiten,
Daten in einem Datentreiber zu binden. Die Knoten 'Excel Datei',
'CSV Datei', 'Datenbank' und 'Datenschleife' werden
alle ausführlich in Abschnitt 38.4 erläutert.
Außerdem können Daten durch Aufruf der 'Prozeduren'
qfs.databinder.bindList
oder qfs.databinder.bindSets
in der
Standardbibliothek qfs.qft
gebunden werden. Als Parameter werden Listen oder
Sätze von Werten in Form von Texten übergeben, die aufgeteilt werden, um über die Werte
zu iterieren. Informationen zur Standardbibliothek finden Sie in Kapitel 8 des Tutorials.
Und schließlich können Daten aus Jython (und analog aus Groovy bzw. JavaScript) auch direkt mit Hilfe
des databinder
Moduls gebunden werden, welches folgende Methoden bietet:
|
|
|
void bindDict(Object rc, String loopname, dictionary dict, String counter=None, String intervals=None) |
Parameter |
rc | Der aktuelle Runcontext. |
loopname |
Der Name unter dem die Daten gebunden werden, entsprechend dem Attribut 'Name' eines
'Daten' Knotens.
|
dict | Das zu bindende Dictionary. |
counter |
Ein optionaler Variablenname für den Iterationszähler.
|
intervals |
Optionale Bereiche von Indizes, getrennt durch Komma, z.B. "0,2-3".
|
|
void bindList(Object rc, String loopname, String varname, Object values, String separator=None, String counter=None, String intervals=None) |
Parameter |
rc | Der aktuelle Runcontext. |
loopname |
Der Name unter dem die Daten gebunden werden, entsprechend dem Attribut 'Name' eines
'Daten' Knotens.
|
varname | Der Name der zu bindenden Variable. |
values |
Die zu bindenden Werte. Entweder eine Sequenz oder ein Text, der aufgeteilt wird.
|
separator |
Optionales Trennzeichen für die Aufteilung der Werte, falls sie als Text übergeben
werden. Standard ist Leerraum.
|
counter |
Ein optionaler Variablenname für den Iterationszähler.
|
intervals |
Optionale Bereiche von Indizes, getrennt durch Komma, z.B. "0,2-3".
|
|
void bindSets(Object rc, String loopname, Object varnames, Object values, String separator=None, String counter=None, String intervals=None) |
Parameter |
rc | Der aktuelle Runcontext. |
loopname |
Der Name unter dem die Daten gebunden werden, entsprechend dem Attribut 'Name' eines
'Daten' Knotens.
|
varnames |
Die Namen der zu bindenden Variablen. Entweder eine Sequenz, oder ein Text, der
aufgeteilt wird.
|
values |
Die Sätze von zu bindenden Werten. Entweder eine Sequenz von Sequenzen, wobei jede
innere Sequenz einem Satz von Daten entspricht, oder ein Text der aufgeteilt wird.
|
separator |
Optionales Trennzeichen für die Aufteilung der Variablen und der Sätze von Werten,
falls diese als Text übergeben werden. Standard ist Leerraum. Sätze von Werten werden
jeweils durch Zeilenumbrüche getrennt.
|
counter |
Ein optionaler Variablenname für den Iterationszähler.
|
intervals |
Optionale Bereiche von Indizes, getrennt durch Komma, z.B. "0,2-3".
|
|
|
|
Einige Beispiele:
|
import databinder
# Three iterations with the values "spam", "bacon" and "eggs"
# bound to the variable named "ingredient"
databinder.bindList(rc, "meal", "ingredient", ["spam", "bacon", "eggs"])
# Same with string values
databinder.bindList(rc, "meal", "ingredient", "spam bacon eggs")
# Same with string values and special separator
databinder.bindList(rc, "meal", "ingredient", "spam|bacon|eggs", "|")
# Two iterations, the first with item="apple" and number="5",
# the second with item="orange" and number="3"
databinder.bindSets(rc, "fruit", ["item", "number"],
[["apple",5], ["orange",3]])
# Same with string values, note the linebreak
databinder.bindSets(rc, "fruit", "item number", """apple 5
orange 3""")
# Same as before with the data stored in a dict
databinder.bindDict(rc, "fruit",
{"item": ["apple", "orange"],
"number": [5,3]}) |
|
| | Beispiel 20.1: Beispiele für die Verwendung des databinder Moduls | |