Aufbau eines Tests – expects
Im nachfolgenden Blog möchte ich euch den Aufbau eines Tests erklären, was expects sind und wie wir mit ihnen arbeiten.
Wir werden das Ganze gemeinsam an einem kleinen Beispiel einer Rechenfunktion Schritt für Schritt durchspielen.
Der Test
Unsere Testprozeduren beinhalten den Aufruf der zu testenden Programmeinheit und einige Überprüfungen des erzeugten Ergebnisses. Wie umfangreich und detailliert diese Überprüfungen ausfallen sind vom Entwickler oder dem Unternehmen selbst abhängig, wir zeigen hier nur die Möglichkeiten auf, die genutzt werden können anhand eines kleinen Beispieles.
Prüfungen – expects
Kommen wir nun also zum Herzstück unserer Tests – unsere expects. Diese expects führen den Vergleich zwischen dem erhaltenen Wert und dem erwarteten Wert durch und melden das entsprechende Ergebnis. Wir haben auch die Möglichkeit einen optionalen Text zu übergeben, welcher im Fehlerfall angezeigt wird um zum Beispiel einen Hinweis auf die Art des Fehlers zu geben. Dies würde ich immer empfehlen, da man so besser identifizieren kann was schief läuft.
Der Aufbau ist wie folgt:
ut.expect(<Ergebnis> [, ‚Fehlermeldung‘ ] ) . <Vergleicher>( <Erwartung>);
Eine Hand voll Beispiele, welche alle zu einem positiven Ergebnis im Unit-Test führen würden.
1 2 3 4 |
ut.expect( 'Hallo Welt!' ).to_equal( 'Hallo Welt!' ); ut.expect( 'Lorem_impsum' ).to_be_like( '%rem%' ); ut.expect( 3 ).to_be_between( 1, 3 ); ut.expect( ( 1 = 0 ) ).to_be_false(); |
Das Ganze geht auch in der negierten Form. indem man ein „not_“ davor stellt.
1 |
ut.expect( 'Hallo Welt!' ).not_to_equal( 'Hallo Welt!2' ); |
Wichtig ist zu sagen, dass der Datentyp beim Vergleichen auf beiden Seiten gleich sein muss.
Folgendes
1 |
ut.expect(3).to_equal('3'); |
würde mit der Fehlermeldung „Actual (number) cannot be compared to Expected (varchar2) using matcher equal“ quittiert und die Prüfung wird als fehlschlag markiert.
Auflistung der verfügbaren Vergleicher.
- be_between
- be_empty
- be_false
- be_greater_than
- be_greater_or_equal
- be_less_or_equal
- be_less_than
- be_like
- be_not_null
- be_null
- be_true
- equal
- match
Da die Liste recht lang ist, würde ich euch noch einmal das Cheat Sheet von Jacek Gebal ans Herz legen. Gute Auflistung mit jeweils einem Beispiel. Ausdrucken und bei euch auf den Platz legen! :D
unsere Beispielfunktion – calculate_it
Damit das Ganze etwas anschaulicher wird, wollen wir eine Funktion testen.
Unser Auftraggeber möchte zwei Zahlen und als dritten Parameter die Bestimmung der Rechenoperation übergeben. Wir werden hier zur Veranschaulichung nur die Subtraktion zeigen. Soweit zu unserer Funktion.
Ich möchte hier gleich eine Lanze für Test-driven Development (TDD) brechen und entsprechende Ansatz verfolgen. Erst der Test, dann die Funktion.
Bevor wir den Test schreiben, brauchen wir jedoch eine Hülle für unsere Funktion, damit wir dagegen kompilieren können. Diese Hülle ist leer, da wir ja zuerst den Test und dann die Funktion programmieren.
1 2 3 4 5 6 |
create or replace FUNCTION calculate_it( operand_1 NUMBER, operand_2 NUMBER, operator VARCHAR2 ) RETURN NUMBER IS BEGIN RETURN NULL; END; |
Und nun noch unsere Hülle für unser Unit-Test.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
create or replace PACKAGE test_calculate_it IS -- %suite(Tests zu calculate_it) -- %test(Grundfunktionen) PROCEDURE grundfunktion; END; / create or replace PACKAGE BODY test_calculate_it IS PROCEDURE grundfunktion IS v_result NUMBER; BEGIN NULL; END grundfunktion; END test_calculate_it; / |
Damit ist der Grundstein gelegt und wir gucken mal, ob der Test läuft. Natürlich führen wir bei der Entwicklung gezielt unser Testpackage aus, da eine komplette Ausführung aller Tests im Schema nicht notwendig ist.
1 |
exec ut.run('test_calculate_it'); |
1 2 3 4 5 |
Tests zu calculate_it Grundfunktionen [,002 sec] Finished in ,002872 seconds 1 tests, 0 failed, 0 errored, 0 disabled, 0 warning(s) |
Jetzt können wir anfangen unsere erste Prüfung einzubauen. Wir lassen die Funktion das Ergebnis in eine Variable geben und diese werden wir mit unserem Framework vergleichen lassen. In unserem Beispiel nutzen wir den Vergleicher to_equal vom Framework. Nicht zu vergessen unseren Text für die Fehlermeldung.
1 2 3 4 5 6 7 8 9 10 |
create or replace PACKAGE BODY test_calculate_it IS PROCEDURE grundfunktion IS v_result NUMBER; BEGIN v_result := calculate_it(5, 2, '-'); ut.expect(v_result, 'Fehlerhafte Rueckgabe.').to_equal(3); END grundfunktion; END test_calculate_it; |
Und die Ausgabe – logischerweise noch fehlerhaft.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
Tests zu calculate_it Grundfunktionen [,329 sec] (FAILED - 1) Failures: 1) grundfunktion "Fehlerhafte Rueckgabe." Actual: NULL (number) was expected to equal: 3 (number) at "SOC.TEST_CALCULATE_IT", line 8 ut.expect(v_result, 'Fehlerhafte Rueckgabe.').to_equal(3); Finished in ,329965 seconds 1 tests, 1 failed, 0 errored, 0 disabled, 0 warning(s) |
Da unsere Funktion ja noch „NULL“ zurück gibt, stimmt der Vergleich natürlich nicht und wir bekommen den Fehler angezeigt.
Jetzt haben wir einen Stand, wo wir jederzeit unseren Test ausführen und damit prüfen können, ob wir mit unserer Entwicklung den richtien Weg gehen. Ich habe hier absichtlich nur eine rudimentäre Prüfung eingebaut, da der Fokus des Blogeintrags auf utPLSQL und nicht TDD liegt.
Machen wir uns also an die Arbeit unsere Funktion zu schreiben, damit der Test „grün“ wird.
1 2 3 4 5 6 7 8 9 10 11 12 |
create or replace FUNCTION calculate_it( operand_1 NUMBER, operand_2 NUMBER, operator VARCHAR2 ) RETURN NUMBER IS v_return NUMBER; BEGIN IF operator = '-' THEN v_return := operand_2 - operand_1; END IF; RETURN v_return; END; |
Und schauen wir uns die Ausgabe unseres Tests an.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
Tests zu calculate_it Grundfunktionen [,023 sec] (FAILED - 1) Failures: 1) grundfunktion "Fehlerhafte Rueckgabe." Actual: -3 (number) was expected to equal: 3 (number) at "SOC.TEST_CALCULATE_IT", line 8 ut.expect(v_result, 'Fehlerhafte Rueckgabe.').to_equal(3); Finished in ,024684 seconds 1 tests, 1 failed, 0 errored, 0 disabled, 0 warning(s) |
Hmm, das Ergebnis stimmt noch nicht, habe ich wohl die Operanden vertauscht. Dann mal korrigieren.
1 2 3 4 5 6 7 8 9 10 11 12 |
create or replace FUNCTION calculate_it( operand_1 NUMBER, operand_2 NUMBER, operator VARCHAR2 ) RETURN NUMBER IS v_return NUMBER; BEGIN IF operator = '-' THEN v_return := operand_1 - operand_2; END IF; RETURN v_return; END; |
Und austesten.
1 2 3 4 5 6 7 8 |
Tests zu calculate_it Grundfunktionen [,002 sec] Finished in ,002992 seconds 1 tests, 0 failed, 0 errored, 0 disabled, 0 warning(s) PL/SQL-Prozedur erfolgreich abgeschlossen. |
Wundervoll, es geht. Wie man sieht, sind die Ausgabe im Gutfall deutlich kleiner. Man möchte ja auch nicht sehen was richtig ist, sonder was schief läuft.
Damit ist unser Beispiel abgeschlossen. Wenn ihr mögt, könnt ihr natürlich die Funktion um weitere Operatoren oder sogar Operanden erweitern. Fehlermeldungen wäre sicher auch nicht schlecht, doch das überlasse ich eurer Kreativität.
SCHREIBEN SIE EINEN KOMMENTAR