Hindernis beim Code-Beitrag: `test::nginx`
API7.ai
November 17, 2022
Testing ist ein wesentlicher Bestandteil der Softwareentwicklung. Das Konzept der Testgetriebenen Entwicklung (TDD) ist so populär geworden, dass fast jedes Softwareunternehmen ein QA-Team (Quality Assurance) hat, das sich um die Testarbeit kümmert.
Testing ist der Eckpfeiler der Qualität und des guten Rufs von OpenResty, aber es ist auch der am meisten vernachlässigte Teil der Open-Source-Projekte von OpenResty. Viele Entwickler verwenden das lua-nginx-module
täglich und führen gelegentlich ein Flame Graph aus, aber wie viele Leute führen die Testfälle aus? Sogar viele OpenResty-basierte Open-Source-Projekte haben keine Testfälle. Aber ein Open-Source-Projekt ohne Testfälle und kontinuierliche Integration ist nicht vertrauenswürdig.
Im Gegensatz zu kommerziellen Unternehmen gibt es jedoch in den meisten Open-Source-Projekten keine spezialisierten Softwaretestingenieure. Wie stellen sie also die Qualität ihres Codes sicher? Die Antwort ist einfach: "Testautomatisierung" und "kontinuierliche Integration", wobei die Schlüsselpunkte Automatisierung und Kontinuität sind, beides hat OpenResty in größtmöglichem Umfang erreicht.
OpenResty hat 70 Open-Source-Projekte, und ihre Unit-Tests, Integrationstests, Leistungstests, Mock-Tests, Fuzz-Tests und andere Arbeitslasten sind schwer zu lösen, wenn sie rein manuell von den Community-Mitwirkenden durchgeführt werden. Daher hat OpenResty von Anfang an mehr in die Automatisierungstests investiert. Dies mag kurzfristig das Projekt verlangsamen, aber langfristig ist die Investition in diesem Bereich sehr kosteneffektiv. Wenn ich also mit anderen Ingenieuren über die Logik und das Toolset von OpenResty für Tests spreche, sind sie erstaunt.
Lassen Sie uns über die Testphilosophie von OpenResty sprechen.
Konzept
test::nginx
ist das Kernstück der OpenResty-Testarchitektur, das von OpenResty selbst und den umliegenden lua-resty
-Bibliotheken verwendet wird, um Testsets zu organisieren und zu schreiben. Es ist ein Testframework mit einer sehr hohen Einstiegshürde. Der Grund dafür ist, dass test::nginx
im Gegensatz zu gängigen Testframeworks nicht auf Assertions basiert und nicht die Lua-Sprache verwendet, was Entwickler dazu zwingt, test::nginx
von Grund auf zu lernen und ihr vorhandenes Wissen über Testframeworks umzukehren.
Ich kenne mehrere OpenResty-Mitwirkende, die C- und Lua-Code an OpenResty übermitteln können, aber es schwierig finden, Testfälle mit test::nginx
zu schreiben. Sie wussten entweder nicht, wie man sie schreibt, oder wie man sie repariert, wenn sie auf Testfehler stoßen. Daher bezeichne ich test::nginx
als ein Hindernis bei der Code-Beitrag.
test::nginx
kombiniert Perl, datengetriebene und DSL (Domain-specific language). Für denselben Testfall-Satz können Sie durch die Steuerung der Parameter und Umgebungsvariablen unterschiedliche Effekte wie zufällige Ausführung, mehrfache Wiederholungen, Speicherleckerkennung, Stresstests usw. erreichen.
Installation und Beispiele
Bevor wir test::nginx
verwenden, lernen wir, wie man es installiert.
Was die Softwareinstallation im OpenResty-System betrifft, ist nur die offizielle CI-Installationsmethode die zeitnahste und effektivste; andere Installationsmethoden stoßen immer auf verschiedene Probleme. Deshalb empfehle ich Ihnen, offizielle Methoden als Referenz zu nehmen, wo Sie auch die Installation und Verwendung von test::nginx
finden. Es gibt vier Schritte.
- Installieren Sie zunächst den Perl-Paketmanager
cpanminus
. - Installieren Sie dann
test::nginx
übercpanm
.
sudo cpanm --notest Test::Nginx IPC::Run > build.log 2>&1 || (cat build.log && exit 1)
- Klonen Sie als nächstes den neuesten Quellcode.
git clone https://github.com/openresty/test-nginx.git
- Laden Sie schließlich die
test-nginx
-Bibliothek über den Perl-Befehlprove
und führen Sie den Satz von Testfällen im Verzeichnis/t
aus.
prove -Itest-nginx/lib -r t
Nach der Installation schauen wir uns den einfachsten Testfall in test::nginx
an. Der folgende Code ist an die offizielle Dokumentation angepasst, und ich habe alle angepassten Steuerparameter entfernt.
use Test::Nginx::Socket 'no_plan';
run_tests();
__DATA__
=== TEST 1: set Server
--- config
location /foo {
echo hi;
more_set_headers 'Server: Foo';
}
--- request
GET /foo
--- response_headers
Server: Foo
--- response_body
hi
Obwohl test::nginx
in Perl geschrieben ist und als eines der Module funktioniert, können Sie etwas in Perl oder einer anderen Sprache aus dem obigen Test erkennen? Richtig. Es liegt daran, dass test::nginx
die eigene Perl-Implementierung von DSL des Autors ist, speziell für das Testen von NGINX und OpenResty abstrahiert.
Wenn wir also zum ersten Mal diese Art von Test sehen, verstehen wir ihn höchstwahrscheinlich nicht. Aber keine Sorge; lassen Sie uns den obigen Testfall analysieren.
Zunächst einmal, use Test::Nginx::Socket;
, das ist die Art und Weise, wie Perl Bibliotheken referenziert, genau wie require
in Lua. Dies erinnert uns auch daran, dass test::nginx
ein Perl-Programm ist.
Die zweite Zeile, run_tests();
ist eine Perl-Funktion in test::nginx
, die Einstiegsfunktion für das Testframework. Wenn Sie andere Perl-Funktionen in test::nginx
aufrufen möchten, müssen sie vor run_tests
platziert werden, um gültig zu sein.
Das __DATA__
in der dritten Zeile ist ein Flag, das anzeigt, dass alles darunter Testdaten sind, und Perl-Funktionen sollten vor diesem Flag abgeschlossen sein.
Das nächste === TEST 1: set Server
, der Titel des Testfalls, gibt den Zweck dieses Tests an, und es gibt ein Tool, das die Nummerierung automatisch arrangiert.
--- config
ist das NGINX-Konfigurationsfeld. Im obigen Fall haben wir NGINX-Befehle verwendet, nicht Lua, und wenn Sie Lua-Code hinzufügen möchten, würden Sie dies hier mit einer Direktive wie content_by_lua
tun.
--- request
wird verwendet, um ein Terminal zu simulieren, das eine Anfrage sendet, gefolgt von GET /foo
, das die Methode und den URI der Anfrage angibt.
--- response_headers
, das wird verwendet, um Antwortheader zu überprüfen. Das folgende Server: Foo
gibt den header
und value
an, die in den Antwortheadern erscheinen müssen. Wenn nicht, schlägt der Test fehl.
Der letzte --- response_body
, wird verwendet, um den entsprechenden Body zu überprüfen. Das folgende hi
ist die Zeichenkette, die im Antwortbody erscheinen muss; wenn nicht, schlägt der Test fehl.
Nun, hier ist die Analyse des einfachsten Testfalls abgeschlossen. Das Verständnis des Testfalls ist also eine Voraussetzung für die Erledigung von OpenResty-bezogenen Entwicklungsarbeiten.
Schreiben Sie Ihre Testfälle
Als nächstes ist es an der Zeit, mit dem praktischen Testen zu beginnen. Erinnern Sie sich, wie wir im letzten Artikel den Memcached-Server getestet haben? Richtig; wir haben resty
verwendet, um die Anfrage manuell zu senden, was durch den folgenden Code dargestellt wird.
resty -e 'local memcached = require "resty.memcached"
local memc, err = memcached:new()
memc:set_timeout(1000) -- 1 sec
local ok, err = memc:connect("127.0.0.1", 11212)
local ok, err = memc:set("dog", 32)
if not ok then
ngx.say("failed to set dog: ", err)
return
end
local res, flags, err = memc:get("dog")
ngx.say("dog: ", res)'
Aber ist das manuelle Senden nicht intelligent genug? Keine Sorge. Wir können versuchen, manuelle Tests in automatisierte umzuwandeln, nachdem wir test::nginx
gelernt haben. Zum Beispiel:
use Test::Nginx::Socket::Lua::Stream;
run_tests();
__DATA__
=== TEST 1: basic get and set
--- config
location /test {
content_by_lua_block {
local memcached = require "resty.memcached"
local memc, err = memcached:new()
if not memc then
ngx.say("failed to instantiate memc: ", err)
return
end
memc:set_timeout(1000) -- 1 sec
local ok, err = memc:connect("127.0.0.1", 11212)
local ok, err = memc:set("dog", 32)
if not ok then
ngx.say("failed to set dog: ", err)
return
end
local res, flags, err = memc:get("dog")
ngx.say("dog: ", res)
}
}
--- stream_config
lua_shared_dict memcached 100m;
--- stream_server_config
listen 11212;
content_by_lua_block {
local m = require("memcached-server")
m.go()
}
--- request
GET /test
--- response_body
dog: 32
--- no_error_log
[error]
In diesem Testfall habe ich --- stream_config
, --- stream_server_config
, --- no_error_log
als Konfigurationselemente hinzugefügt, aber sie sind im Wesentlichen gleich, d.h.
Die Daten und Tests der Tests werden abgestrippt, um die Lesbarkeit und Erweiterbarkeit durch die Abstraktion der Konfiguration zu verbessern.
Hier unterscheidet sich test::nginx
grundlegend von anderen Testframeworks. Diese DSL ist ein zweischneidiges Schwert, da sie die Testlogik klar und leicht erweiterbar macht. Sie erhöht jedoch die Lernkosten, da Sie neue Syntax und Konfiguration neu lernen müssen, bevor Sie mit dem Schreiben von Testfällen beginnen können.
Zusammenfassung
Das test::nginx
ist leistungsstark, aber oft passt es nicht immer zu Ihrem Szenario. Warum mit Kanonen auf Spatzen schießen? In OpenResty haben Sie auch die Möglichkeit, das Assertion-basierte Testframework busted
zu verwenden. Das busted
kombiniert mit resty
wird zu einem Befehlszeilentool und kann auch viele Testanforderungen erfüllen.
Zum Schluss stelle ich Ihnen eine Frage. Können Sie diesen Test für Memcached
lokal ausführen? Wenn Sie einen neuen Testfall hinzufügen können, wäre das großartig.