Unbekannte Verwendung von `test::nginx`
API7.ai
November 24, 2022
In den vorherigen beiden Artikeln haben Sie die meisten Verwendungsmöglichkeiten von test::nginx
gemeistert, und ich bin sicher, dass Sie die meisten Testfallsätze im OpenResty-Projekt verstehen können. Dies ist mehr als genug, um OpenResty und seine umliegenden Bibliotheken zu lernen.
Aber wenn Sie daran interessiert sind, ein OpenResty-Code-Mitarbeiter zu werden, oder wenn Sie test::nginx
verwenden, um Testfälle in Ihren Projekten zu schreiben, dann müssen Sie einige fortgeschrittenere und komplexere Verwendungsmöglichkeiten lernen.
Der heutige Artikel wird wahrscheinlich der "unpopulärste" Teil der Serie sein, weil es etwas ist, das noch nie jemand zuvor geteilt hat. Nehmen Sie lua-nginx-module
, das Kernmodul in OpenResty, als Beispiel, das weltweit über 70 Mitwirkende hat, aber nicht jeder Mitwirkende hat einen Testfall geschrieben. Wenn Sie also den heutigen Artikel lesen, wird Ihr Verständnis von test::nginx
weltweit zu den Top 100 gehören.
Debugging im Test
Zuerst schauen wir uns einige der einfachsten und am häufigsten verwendeten Abschnitte an, die Entwickler beim normalen Debugging verwenden. Hier werden wir die Verwendungsszenarien dieser debug-bezogenen Abschnitte einzeln vorstellen.
ONLY
Sehr oft fügen wir einen neuen Testfall zu dem ursprünglichen Satz von Testfällen hinzu. Wenn die Testdatei viele Testfälle enthält, ist es zeitaufwendig, sie durchzulaufen, besonders wenn Sie die Testfälle wiederholt ändern müssen.
Gibt es also eine Möglichkeit, nur einen der von Ihnen angegebenen Testfälle auszuführen? Dies kann leicht mit dem ONLY
-Abschnitt erreicht werden.
=== TEST 1: sanity
=== TEST 2: get
--- ONLY
Der obige Pseudocode zeigt, wie man diesen Abschnitt verwendet. Indem Sie --- ONLY
in die letzte Zeile des Testfalls setzen, der allein ausgeführt werden soll, werden alle anderen Testfälle ignoriert, wenn Sie prove
verwenden, um die Testfalldatei auszuführen, und nur dieser eine Test wird ausgeführt.
Dies ist jedoch nur angebracht, wenn Sie debuggen. Wenn der prove
-Befehl den ONLY
-Abschnitt findet, wird er Sie auch daran erinnern, ihn nicht zu vergessen, wenn Sie Ihren Code einreichen.
SKIP
Die Anforderung, die der Ausführung nur eines Testfalls entspricht, ist das Ignorieren eines bestimmten Testfalls. Der SKIP
-Abschnitt wird typischerweise verwendet, um Funktionen zu testen, die noch nicht implementiert sind:
=== TEST 1: sanity
=== TEST 2: get
--- SKIP
Wie Sie in diesem Pseudocode sehen können, ist seine Verwendung ähnlich wie bei ONLY
. Da wir testgetriebene Entwicklung betreiben, müssen wir zuerst Testfälle schreiben; und wenn wir die Implementierung gemeinsam codieren, müssen wir möglicherweise die Implementierung einer Funktion aufgrund der Schwierigkeit oder Priorität der Implementierung verzögern. Dann können Sie den entsprechenden Testfallsatz zunächst überspringen und den SKIP
-Abschnitt entfernen, wenn die Implementierung abgeschlossen ist.
LAST
Ein weiterer häufiger Abschnitt ist LAST
, der ebenfalls einfach zu verwenden ist, da die Testfälle davor ausgeführt werden und die danach ignoriert werden.
=== TEST 1: sanity
=== TEST 2: get
--- LAST
=== TEST 3: set
Sie fragen sich vielleicht, ich verstehe die Bedeutung von ONLY
und SKIP
, aber was ist der Nutzen von LAST
? Tatsächlich haben Ihre Testfälle manchmal Abhängigkeiten, und Sie müssen die ersten paar Testfälle ausführen, bevor die nachfolgenden Tests sinnvoll sind. In diesem Fall ist LAST
sehr nützlich, wenn Sie weiter debuggen.
plan
Von allen test::nginx
-Funktionen ist plan
eine der ärgerlichsten und schwersten zu verstehen. Es stammt aus dem Perl-Modul Test::Plan
, dessen Dokumentation nicht in test::nginx
enthalten ist, und es ist nicht einfach, eine Erklärung dafür zu finden. Daher werde ich es frühzeitig einführen. Ich habe mehrere OpenResty-Code-Mitarbeiter gesehen, die in dieses Loch gefallen sind und nicht einmal herauskommen konnten.
Hier ist ein Beispiel für eine ähnliche Konfiguration, die Sie am Anfang jeder Datei im offiziellen OpenResty-Testsatz sehen können:
plan tests => repeat_each() * (3 * blocks());
Die Bedeutung von plan
hier ist, wie viele Tests gemäß dem Plan in der gesamten Testdatei durchgeführt werden sollten. Wenn das Ergebnis des endgültigen Laufs nicht mit dem Plan übereinstimmt, schlägt der Test fehl.
Für dieses Beispiel, wenn der Wert von repeat_each
2
ist und es 10 Testfälle gibt, dann sollte der Wert von plan
2 x 3 x 10 = 60
sein. Das Einzige, was Sie möglicherweise verwirrt, ist die Bedeutung der Zahl 3
, die wie eine magische Zahl aussieht!
Machen Sie sich keine Sorgen, lassen Sie uns weiter das Beispiel betrachten, Sie werden es in Kürze herausfinden. Können Sie zunächst den richtigen Wert von plan im folgenden Testfall herausfinden?
=== TEST 1: sanity
--- config
location /t {
content_by_lua_block {
ngx.say("hello")
}
}
--- request
GET /t
--- response_body
hello
Ich glaube, jeder würde schließen, dass plan = 1
, da der Test nur das response_body
überprüft.
Aber das ist nicht der Fall! Die richtige Antwort ist, dass plan = 2
. Warum? Weil test::nginx
eine implizite Überprüfung hat, d.h. --- error_code: 200
, die standardmäßig überprüft, ob der HTTP-Antwortcode 200
ist.
Die magische Zahl 3
oben bedeutet also wirklich, dass jeder Test zweimal explizit überprüft wird, zum Beispiel für body
und error log
, und implizit für response code
.
Da dies so fehleranfällig ist, empfehle ich Ihnen, plan
mit der folgenden Methode auszuschalten.
use Test::Nginx::Socket 'no_plan';
Wenn Sie es nicht ausschalten können, zum Beispiel, wenn Sie auf einen ungenauen plan
im offiziellen OpenResty-Testsatz stoßen, wird empfohlen, dass Sie nicht nach der Ursache forschen, sondern einfach Zahlen zu den Ausdrücken von plan
hinzufügen oder subtrahieren.
plan tests => repeat_each() * (3 * blocks()) + 2;
Dies ist auch die offizielle Methode, die verwendet wird.
Preprocessor
Wir wissen, dass es möglicherweise einige öffentliche Einstellungen zwischen verschiedenen Testfällen derselben Testdatei gibt. Wenn die Einstellungen in jedem Testfall wiederholt werden, wird der Code redundant und später schwer zu ändern.
An diesem Punkt können Sie die add_block_preprocessor
-Direktive verwenden, um ein Stück Perl-Code hinzuzufügen, wie zum Beispiel das Folgende:
add_block_preprocessor(sub {
my $block = shift;
if (!defined $block->config) {
$block->set_value("config", <<'_END_');
location = /t {
echo $arg_a;
}
_END_
}
});
Dieser Preprocessor fügt allen Testfällen einen config
-Abschnitt hinzu, und der Inhalt ist location /t
, so dass Sie in Ihren späteren Testfällen das config
weglassen und direkt darauf zugreifen können.
=== TEST 1:
--- request
GET /t?a=3
--- response_body
3
=== TEST 2:
--- request
GET /t?a=blah
--- response_body
blah
Benutzerdefinierte Funktionen
Zusätzlich zum Hinzufügen von Perl-Code zum Preprocessor können Sie auch beliebig Perl-Funktionen oder benutzerdefinierte Funktionen, wie wir sie nennen, vor der run_tests
-Funktion hinzufügen.
Hier ist ein Beispiel, das eine Funktion hinzufügt, die eine Datei liest und sie mit der eval
-Direktive kombiniert, um eine POST
-Datei zu implementieren:
sub read_file {
my $infile = shift;
open my $in, $infile
or die "cannot open $infile for reading: $!";
my $content = do { local $/; <$in> };
close $in;
$content;
}
our $CONTENT = read_file("t/test.jpg");
run_tests;
__DATA__
=== TEST 1: sanity
--- request eval
"POST /\n$::CONTENT"
Shuffle
Zusätzlich zu den oben genannten Punkten hat test::nginx
eine wenig bekannte Falle: Es führt Testfälle standardmäßig in zufälliger Reihenfolge aus, anstatt der Reihenfolge und Nummerierung der Testfälle zu folgen.
Es war ursprünglich gedacht, um mehr Probleme zu testen. Schließlich wird der NGINX-Prozess nach jedem Testfall geschlossen und ein neuer NGINX-Prozess gestartet, um ihn auszuführen, so dass die Ergebnisse nicht von der Reihenfolge abhängig sein sollten.
Für Projekte auf niedriger Ebene ist dies wahr. Für Projekte auf Anwendungsebene gibt es jedoch externe persistente Speicher wie Datenbanken. Eine willkürliche Ausführung kann zu falschen Ergebnissen führen. Da es jedes Mal zufällig ist, kann es einen Fehler melden oder nicht, und der Fehler kann jedes Mal anders sein. Dies verursacht offensichtlich Verwirrung für Entwickler, einschließlich mir, da ich hier oft gestolpert bin.
Daher mein Rat: Bitte schalten Sie diese Funktion aus. Sie können sie mit den folgenden zwei Codezeilen ausschalten:
no_shuffle();
run_tests;
Insbesondere wird no_shuffle
verwendet, um die Zufälligkeit zu deaktivieren und die Tests streng in der Reihenfolge der Testfälle auszuführen.
reindex
Der Testfallsatz von OpenResty hat strenge Formatierungsanforderungen. Jeder Testfall muss durch drei neue Zeilen getrennt sein, und die Testfallnummerierung muss streng selbstwachsend sein.
Glücklicherweise haben wir ein automatisches Tool, reindex
, um diese langweiligen Dinge zu erledigen, das im openresty-devel-utils-Projekt versteckt ist. Da es keine Dokumentation darüber gibt, wissen nur wenige Leute davon.
Wenn Sie interessiert sind, können Sie versuchen, die Testfallnummerierung durcheinander zu bringen oder die Anzahl der Zeilenumbrüche hinzuzufügen oder zu entfernen und dann dieses Tool verwenden, um es zu sortieren und zu sehen, ob Sie es wiederherstellen können.
Zusammenfassung
Dies ist das Ende der Einführung in test::nginx
. Natürlich gibt es noch mehr Funktionen, wir haben nur über die Kern- und wichtigsten gesprochen. "Gib einem Mann einen Fisch und du ernährst ihn für einen Tag; lehre ihn, wie man fischt, und du ernährst ihn für ein Leben lang." Ich habe Ihnen die grundlegenden Methoden und Vorsichtsmaßnahmen für das Lernen von Tests beigebracht, dann können Sie in den offiziellen Testfallsatz eintauchen, um ein besseres Verständnis zu erlangen.
Abschließend denken Sie bitte über die folgenden Fragen nach. Gibt es Tests in Ihrer Projektentwicklung? Und welches Framework verwenden Sie zum Testen? Wir freuen uns, wenn Sie diesen Artikel mit mehr Menschen teilen, um gemeinsam auszutauschen und zu lernen.