Verifikation von Testergebnissen
Eine ausführlichere Darstellung dieses Prinzips findet sich in Zentralisierte Erfassung der Testereignisse (TestEvents).
Zusammen mit einigen intern verwendeten Steuervariablen ergibt sich daraus folgendes Bild:
TTB_EXP("Number: 2";)Neben der Übereinstimmung des Ereignis-Strings selbst können unterschiedliche Anforderungen bzgl. der erlaubten Reihenfolgen für die aufgezeichneten Ereignisse bestehen. Für die korrekte Auswahl der verfügbaren Verifikations-Algorithmen ist ein Grundverständnis der prinzipiellen Abläufe hilfreich. Die folgenden Abschnitte geben dazu die notwendige Hilfestellung.
Dies ist häufig dann der Fall, wenn im betrachteten System
Da das erwartete 1. Ereignis mit dem 1. Eintrag im Ereignis-Vektor übereinstimmt, wird das Zustandsflag auf true ("OK") gesetzt. Gleichzeitig wird die als nächstes zu prüfende Position inkrementiert.
Analog zum 1. Schritt wird das betreffende Zustandsflag gesetzt und die als nächstes zu prüfende Position inkrementiert.
Da alle Ereignisse wie erwartet eingetroffen sind, wurden alle Zustandsflags auf true ("OK") gesetzt. Die als nächstes zu prüfende Position zeigt "hinter" den letzten Eintrag im Vektor. Falls nachfolgend im Testverlauf ein weiteres Ereignis aufgezeichnet wird, kann es unter dieser Position überprüft werden.
TTB_EXP ("Number: 2"); TTB_EXP ("Number: 4"); TTB_EXP ("double value: 88.88"); TTB_EXP ("Number: 6"); TTB_EXP ("double value: 66.66");
Eine mögliche Ursache kann darin bestehen, dass parallel zu einem sequentiell stattfindenden Ablauf andere Ereignisse aufgrund von Multithreading an zufälliger Stelle "eingestreut" werden.
So kann die unter "Ausgangspunkt" (siehe oben) beschriebene Situation als Überlagerung zweier sequentieller Abläufe verstanden werden:
Da das als erstes erwartete Ereignis mit dem 1. Eintrag im Ereignis-Vektor übereinstimmt, wird das Zustandsflag auf true ("OK") gesetzt. Gleichzeitig wird die als nächstes zu prüfende Position inkrementiert. Der Ablauf ist zunächst identisch wie bei der Prüfung auf strikte Reihenfolge.
Analog zum 1. Schritt wird das betreffende Zustandsflag gesetzt und die als nächstes zu prüfende Position inkrementiert. Auch hier ist der Ablauf zunächst identisch wie bei der Prüfung auf strikte Reihenfolge.
Bei der Suche nach dem dritten Ereignis wird der nicht passende nächste Eintrag an Position 2 übersprungen, anschliessend das Zustandsflag für den passenden Eintrag an der nachfolgenden Position 3 auf true gesetzt. Zuletzt wird die als nächstes zu prüfende Position inkrementiert.
Bevor der erste Eintrag der zweiten Sequenz überprüft wird, muss die als nächstes zu prüfende Check-Posiiton zurückgesetzt werden. Nach dem Zurücksetzen zeigt die Check-Position auf die erste noch nicht überprüfte Position innerhalb des Ereignisvektors.
Bei der Suche nach dem ersten Ereignis wird das Zustandsflag für den passenden Eintrag an der Position 2 auf true gesetzt. Anschliessend wird die als nächstes zu prüfende Position auf Position 4 inkrementiert. Der bereits überprüfte Eintrag 3 wird dabei übersprungen. Dadurch ist gewährleistet, dass die Check-Position immer auf einen noch nicht verifizierten Eintrag zeigt.
Bei der Suche nach dem zweiten Ereignis der zweiten Sequnez wird das Zustandsflag für den passenden Eintrag an der Position 4 auf true gesetzt. Anschliessend wird die als nächstes zu prüfende Position auf die nicht vorhandene Position 5 inkrementiert.
// Check that the small numbers have occured in increasing order TTB_EXP_SEQ ("Number: 2"); TTB_EXP_SEQ ("Number: 4"); TTB_EXP_SEQ ("Number: 6"); // Go back to first unchecked position within the array of events TheTestEvents()->ResetCurrentCheckPosition(); // Check that the big numbers have occured in decreasing order TTB_EXP_SEQ ("double value: 88.88"); TTB_EXP_SEQ ("double value: 66.66");Alternativ kann durchgägig das Makro TTB_EXP verwendet werden:
// Alternatively you can also use TTB_EXP and set the required CheckMode TheTestEvents()->SetCheckType(CheckType::eSEQUENTIAL); // Check that the small numbers have occured in increasing order TTB_EXP ("Number: 2"); TTB_EXP ("Number: 4"); TTB_EXP ("Number: 6"); // Go back to first unchecked position within the array of events TheTestEvents()->ResetCurrentCheckPosition(); // Check that the big numbers have occured in decreasing order TTB_EXP ("double value: 88.88"); TTB_EXP ("double value: 66.66");
Geht man immer nach dem Prinzip der "Maximal-Prüfung" vor, d.h. prüft man immer dann auf Reihenfolgen, wenn sie von der aktuell gewählten Implementierungslösung garantiert werden, so erhält man einen besseren Prüfstein für spätere Refakturierungen. Unbeabsichtigte Veränderungen in der Abarbeitungsreihenfolge werden dann zuverlässig aufgedeckt. Eine Abschwächung der Prüfung bzgl. der Ereignis-Reihenfolgen bzw. eine Umdefinition der Reihenfolgen sollte dann erst bei Bedarf zum Zeitpunkt der Refakturierung erfolgen.
Das als erstes zu prüfende Ereignis befindet sich an der zweiten Stelle des Ereignisvektors. Dementsprechend wird das Zustandsflag am Position 1 auf true ("OK") gesetzt. Die als nächstes zu prüfende Check-Position zeigt unverändert auf Position 0.
Das als nächstes zu prüfende Ereignis befindet sich an der ersten Stelle des Ereignisvektors. Dementsprechend wird das Zustandsflag am Position 0 auf true ("OK") gesetzt. Die als nächstes zu prüfende Check-Position wird auf die nächste noch nicht überprüfte Position 2 gesetzt.
Nach den gleichen Prinzipien werden die restlichen Ereignis-Strings überprüft. Die aktuelle Check-Positzion wird jeweils dann verschoben, wenn ihr Eintrag überprüft wurde. Zuletzt zeigt sie auf Position 5, also hinter den letzten existierenden Eintrag.
// Check that events have have occured in any order TTB_EXP_VAR ("Number: 4"); TTB_EXP_VAR ("Number: 2"); TTB_EXP_VAR ("double value: 66.66"); TTB_EXP_VAR ("Number: 6"); TTB_EXP_VAR ("double value: 88.88");Alternativ kann durchgängig das Makro TTB_EXP verwendet werden:
// Alternatively you can also use TTB_EXP and set the required CheckMode TheTestEvents()->SetCheckType(CheckType::eVARIABLE); // Check that events have have occured in any order TTB_EXP ("Number: 4"); TTB_EXP ("Number: 2"); TTB_EXP ("double value: 66.66"); TTB_EXP ("Number: 6"); TTB_EXP ("double value: 88.88");
Neben der im Diagramm rot dargestellten Fehlermeldung werden zusätzlich im Test-Protokoll Angaben zum aktuellen Zustand des Ereignis-Vektors ausgegeben:
List of stored TestEvents (current check position = 1, used checkType = eSTRICT) Ok 0: Number: 2 ## ? 1: Number: 4 ? 2: double value: 88.88 ? 3: Number: 6 ? 4: double value: 66.66
Neben der im Diagramm rot dargestellten Fehlermeldung werden zusätzlich im Test-Protokoll Angaben zum aktuellen Zustand des Ereignis-Vektors ausgegeben:
List of stored TestEvents (current check position = 3, used checkType = eSEQUENTIAL) Ok 0: Number: 2 ? 1: Number: 4 Ok 2: double value: 88.88 ## ? 3: Number: 6 ? 4: double value: 66.66
Neben der im Diagramm rot dargestellten Fehlermeldung werden zusätzlich im Test-Protokoll Angaben zum aktuellen Zustand des Ereignis-Vektors ausgegeben:
List of stored TestEvents (current check position = 3, used checkType = eVARIABLE) Ok 0: Number: 2 Ok 1: Number: 4 Ok 2: double value: 88.88 ## ? 3: Number: 6 ? 4: double value: 66.66
int pos_2 = TTB_EXP_SEQ ("Number: 2"); TTB_EXP_SEQ ("Number: 4"); TTB_EXP_SEQ ("Number: 6"); int pos_6666 = TTB_EXP_VAR ("double value: 66.66"); TTB_EXP_VAR ("double value: 88.88"); // Check found positions TTB_CHECK_EQUAL (pos_2+4, pos_6666); TTB_CHECK_COND (pos_2 < pos_6666) TTB_CHECK (pos_2, std::less(), pos_6666)
int pos_2; int pos_6666; TTB_EXP_SEQ ("Number: 2"). StorePos(pos_2); TTB_EXP_SEQ ("Number: 4"); TTB_EXP_SEQ ("Number: 6"); TTB_EXP_VAR ("double value: 66.66"). StorePos(pos_6666); TTB_EXP_VAR ("double value: 88.88"); // Check found positions TTB_CHECK_EQUAL (pos_2+4, pos_6666); TTB_CHECK_COND (pos_2 < pos_6666) TTB_CHECK (pos_2, std::less(), pos_6666)
int pos_2; int pos_6666; TTB_EXP_SEQ ("Number: 2"); pos_2=TheTestEvents()->GetLastEventPosition(); TTB_EXP_SEQ ("Number: 4"); TTB_EXP_SEQ ("Number: 6"); TTB_EXP_VAR ("double value: 66.66"); pos_6666=TheTestEvents()->GetLastEventPosition(); TTB_EXP_VAR ("double value: 88.88"); // Check found positions TTB_CHECK_EQUAL (pos_2+4, pos_6666); TTB_CHECK_COND (pos_2 < pos_6666) TTB_CHECK (pos_2, std::less(), pos_6666)
Diese Syntax ist zwar eigentlich zu vermeidender "old style", bietet aber trotzdem eine einfache und praktikable Möglichkeit, um relativ übersichtlich auf kleinem Raum die notwendigen Formatierungen durchzuführen. Die damit einhergehenden, bekannten Sicherheitsrisiken (z.B. versehentliche Verwendung nicht kompatibler Formatflags, falsche Anzahl nachfolgender Parameter) können in Testumgebungen aber in Kauf genommen werden, da sie bei der Testausführung in der Regel sofort entdeckt werden.
Wie bei printf ist die Anzahl der möglichen Parameter variabel. Da die Überprüfung jedoch über Makros vorgenommen werden, muss die jeweilige Anzahl verwendeter Parameter am Ende des Makros ergänzt werden.
TTB_EXP1 ("Number: %d", 2); TTB_EXP2 ("%s: %d", "Number", 4); TTB_EXP_SEQ1 ("Number:%2d", 6); TTB_EXP_SEQ1 ("Number:%s", " 8"); TTB_EXP_SEQ2 ("Number: %c%c", '1', '0'); TTB_EXP_VAR1 ("double value: %s", "88.88"); TTB_EXP_VAR1 ("double value: %.2f", 66.66); TTB_EXP_VAR1 ("double value: %.2f", 44.4423); // values are rounded before compare TTB_EXP_VAR1 ("double value:%6.2f", 22.219); // values are rounded before comparegenerieren folgende Formate:
TTB_EXP ("Number: 2"); TTB_EXP ("Number: 4"); TTB_EXP_SEQ ("Number: 6"); TTB_EXP_SEQ ("Number: 8"); TTB_EXP_SEQ ("Number: 10",); TTB_EXP_VAR ("double value: 88.88"); TTB_EXP_VAR ("double value: 66.66"); TTB_EXP_VAR ("double value: 44.44"); TTB_EXP_VAR ("double value: 22.22);
%s | String-Wert | |
%d | ganzzahliger Wert | |
%8d | ganzzahliger Wert, Ausgabe 8 stellig mit führenden Leerzeichen | |
%.2f | Float-Wert mit 2 Nachkommastellen, Wert wird gerundet | |
%8.2f | Float-Wert mit 2 Nachkommastellen und insgesamt 8 stelliger Anzeige mit führenden Leerzeichen |
Weitere Informationen siehe printf Format-Flags (cplusplus.com)
Unterstützt wird diese Syntax durch entsprechende Makros mit Endung _S: TTB_EXP_S, TTB_EXP_VAR_S, TTB_EXP_SEQ_S.
TTB_EXP_S ("Number: " << 2); TTB_EXP_S ("Number" << ": " << 4); TTB_EXP_SEQ_S ("Number:" << std::setw(2) << 6); TTB_EXP_SEQ_S ("Number:" << " 8"); TTB_EXP_SEQ_S ("Number: " << '1' << '0'); TTB_EXP_VAR_S ("double value: " << "88.88"); TTB_EXP_VAR_S ("double value: " << std::fixed << std::setprecision(2) << 66.66); TTB_EXP_VAR_S ("double value: " << std::fixed << std::setprecision(2) << 44.4423); // values are rounded before compare TTB_EXP_VAR_S ("double value:" << std::setw(6) << std::fixed << std::setprecision(2) << 22.219); // values are rounded before comparegenerieren folgende Formate:
TTB_EXP ("Number: 2"); TTB_EXP ("Number: 4"); TTB_EXP_SEQ ("Number: 6"); TTB_EXP_SEQ ("Number: 8"); TTB_EXP_SEQ ("Number: 10",); TTB_EXP_VAR ("double value: 88.88"); TTB_EXP_VAR ("double value: 66.66"); TTB_EXP_VAR ("double value: 44.44"); TTB_EXP_VAR ("double value: 22.22);
setw(4) | 4-stellige Ausgabe mit führenden Leerzeichen, rechtsbündig | |
fixed | Float-Wert mit Dezimalpunktanzeige, ohne Verwendung von "1e-N" | |
setprecision(3) | Verwendung von 3 Nachkommastellen (Voraussetzung: gleichzeitige Angabe von "fixed" oder "scientific") | |
left | linksbündige Ausgabe | |
boolalpha | Ausgabe von Bool-Ausdrücken als "true" oder "false" |
Weitere Informationen siehe Stream manipulators (cplusplus.com)
Beispiel:
int pos_4; int pos_6; TTB_EXP_S ("Number: " << 2); TTB_EXP_S ("Number" << ": " << 4); pos_4 = TheTestEvents()->GetLastEventPosition(); TTB_EXP_SEQ_S ("Number:" << std::setw(2) << 6); pos_6 = TheTestEvents()->GetLastEventPosition(); TTB_CHECK_COND(pos_6 > pos_4); TTB_CHECK_EQUAL(pos_6,3); TTB_CHECK(pos_6,std::greater(),pos_4);
bool SomeBoolFunc(int i) {return i>25;} TTB_TEST_FUNC (CheckCondition) { int i = 23; int j = 5; // Check bool expression TTB_CHECK_COND(i > j); TTB_CHECK_COND(i > 0); // Check bool variable bool someBoolVariable = true; TTB_CHECK_COND(someBoolVariable); // Check bool function TTB_CHECK_COND(SomeBoolFunc(31)); }
Beispiel für einen Protokollabschnitt:
================================================================================ Test: CheckCondition ================================================================================ CheckCondition Ok: i > j (=true) CheckCondition Ok: i > 0 (=true) CheckCondition Ok: someBoolVariable (=true) CheckCondition Ok: SomeBoolFunc(31) (=true)
Beispiel für Testscript mit fehlschlagendem Check-Makro:
int i = 23; int j = 5; TTB_CHECK_COND(j>=i);Auszug aus dem zugehörigen Protokoll:
!!Error at location c:\test\testcasescomparevalues.cpp (48): (call location of error) CheckCondition NOT OK: j>=i (=false)
Beispiele:
TTB_TEST_FUNC (CheckEqual) { int i = 23; int j = 5; // Check integer expressions TTB_CHECK_EQUAL(i,j+18); // Check bool variables bool someBool = true; bool otherBool = true; TTB_CHECK_EQUAL(someBool,otherBool); // Check bool expression TTB_CHECK_EQUAL(i < j, 2*i==5); // Check double values with given precision double pi = 3.14159265; float f = 3.1435f; TheTestEvents()->SetDoublePrecision(2); TTB_CHECK_EQUAL(pi, f); }
Beispiel für einen Protokollabschnitt:
================================================================================ Test: CheckEqual ================================================================================ CheckEqual Ok: i (=23) == j+18 (=23), used type: int CheckEqual Ok: someBool (=true) == otherBool (=true), used type: bool CheckEqual Ok: i < j (=false) == 2*i==5 (=false), used type: bool CheckEqual Ok: pi (=3.14) == f (=3.14), used type: specialization for double, precision is 2
Beispiel für Testscript mit fehlschlagendem Check-Makro:
int i = 23; int j = 5; // Check integer expressions TTB_CHECK_EQUAL(i-4,j+18); // Check bool variables bool someBool = true; bool otherBool = false; TTB_CHECK_EQUAL(someBool,otherBool); // Check bool expression TTB_CHECK_EQUAL(i>=j, 2*i==5); // Check double values double pi = 3.14159265; float f = 3.1435f; TheTestEvents()->SetDoublePrecision(4); TTB_CHECK_EQUAL(pi, f);Auszug aus dem zugehörigen Protokoll:
!!Error at location c:\test\testcasescomparevalues.cpp (99): (call location of error) CheckEqual NOT OK: i-4 (=19) == j+18 (=23), used type: int left : 19 (i-4) right: 23 (j+18) !!Error at location c:\test\testcasescomparevalues.cpp (104): (call location of error) CheckEqual NOT OK: someBool (=true) == otherBool (=false), used type: bool left : true (someBool) right: false (otherBool) !!Error at location c:\test\testcasescomparevalues.cpp (107): (call location of error) CheckEqual NOT OK: i>=j (=true) == 2*i==5 (=false), used type: bool left : true (i>=j) right: false (2*i==5) !!Error at location c:\test\testcasescomparevalues.cpp (113): (call location of error) CheckEqual NOT OK: pi (=3.1416) == f (=3.1435), used type: specialization for double, precision is 4 left : 3.1416 (pi) right: 3.1435 (f)
bool IsGreaterThan(int left, int right) {return left > right;} TTB_TEST_FUNC (CheckWithCompareFunction) { int i = 23; int j = 5; double pi = 3.14159265; double f = 3.1435f; // User defined compare function (binary predicate) TTB_CHECK(i, IsGreaterThan, j); // Using standard predicates TTB_CHECK(i, std::less_equal(), i); TTB_CHECK(pi, std::less (), f); }
Beispiel für einen Protokollabschnitt:
================================================================================ Test: CheckWithCompareFunction ================================================================================ Check Ok: i (=23) IsGreaterThan j (=5), used type: int Check Ok: i (=23) std::less_equal() i (=23), used type: int Check Ok: pi (=3.14159) std::less () f (=3.1435), used type: double
Beispiel für Testscript mit fehlschlagendem Check-Makro:
int i = 23; int j = 5; double pi = 3.14159265; double f = 3.1435f; // User defined compare function (binary predicate) TTB_CHECK(j, IsGreaterThan, i); // Using standard predicates TTB_CHECK(j, std::greaterAuszug aus dem zugehörigen Protokoll:(), i); TTB_CHECK(pi, std::greater_equal (), f);
!!Error at location c:\test\testcasescomparevalues.cpp (153): (call location of error) Check NOT OK: j (=5) IsGreaterThan i (=23), used type: int left : 5 (j) right: 23 (i) !!Error at location c:\test\testcasescomparevalues.cpp (156): (call location of error) Check NOT OK: j (=5) std::greater() i (=23), used type: int left : 5 (j) right: 23 (i) !!Error at location c:\test\testcasescomparevalues.cpp (157): (call location of error) Check NOT OK: pi (=3.14159) std::greater_equal () f (=3.1435), used type: double left : 3.14159 (pi) right: 3.1435 (f)