PieceofScript ist eine einfache Sprache zum Schreiben von Skripten zum automatischen Testen der HTTP-JSON-API.
Mit PieceofScript können Sie:
- Beschreiben von API-Methoden im YAML-Format mit dem Namen der Methode in einer fast natürlichen Sprache, die zum Lesen von Tests geeignet ist
- flexibel genug, um Modelle im YAML-Format zu beschreiben und daraus zufällige Daten zu generieren
- Schreiben Sie komplexe API-Aufrufskripte in einer einfach zu lesenden Sprache mit einfacher Syntax
- Testergebnisse in den Formaten JUnit und HTML erhalten
Ich habe dieses „Fahrrad“ geschrieben, weil mich die SoapUI-Oberfläche runtergefahren hat. Ich wollte die Tests einfach und klar in einem Texteditor ohne spezielle GUI beschreiben. Außerdem verarbeitet git die riesige XML-Datei, die SoapUI ausgibt, nicht. Daher ist es schwierig, Tests für eine bestimmte Aufgabe in demselben Zweig zu platzieren, in dem die Aufgabe selbst ausgeführt wurde. Die Postman-Oberfläche ist viel schöner, aber bei der Entwicklung nimmt es viel Zeit in Anspruch, Anforderungen dort zu erstellen / zu ändern und sie in der richtigen Reihenfolge zu wiederholen. Ich wollte es automatisieren. Ich habe auch andere Testwerkzeuge untersucht, von denen jedes einen "
tödlichen Fehler "
aufwies. In Übereinstimmung mit dem
NIH-Syndrom habe ich eine IDE eröffnet.
Hier ist was daraus wurde.

Der Interpreter ist in PHP geschrieben und ein Phar-Archiv. Er erfordert die Version PHP 7.2, obwohl er möglicherweise auch unter 7.1 funktioniert. Quellcode und Dokumentation
https://github.com/maximw/PieceofScript . Dokumentation in Entwicklung. Wie sich herausstellte, ist dies der schwierigste und langwierigste Teil.
Testprojekt, Struktur und StartTestskriptAPI-TestmethodenAPI-MethodenaufrufGenerierung von Modellen und TestdatenEingebaute FunktionenTestfälleVariablen und BereicheTypen und OperationenSpeichern von Daten zwischen LäufenAusgabe an stdout und BerichteBeispiele - genug Wörter, zeigen Sie den Code!
Gegebenenfalls Kommentare und Pläne für die ZukunftTestprojekt, Struktur und Start
Das Projekt ist ein Verzeichnis mit einer Reihe von Skriptdateien, API-Methodenbeschreibungsdateien und Testdatengeneratoren.
In der Minimalversion sieht das Projekt folgendermaßen aus:
./tests endpoints.yaml - API generators.yaml - start.pos -
Die Startdatei ist das Skript, mit dem der Testprozess beginnt. Es wird beim Start eingestellt:
pos.phar run ./start.pos --junit=junit_report.xml -vvv --config=config.yaml
Alle relativen Pfade werden aus dem Arbeitsverzeichnis gelesen, das die Startdatei enthält.
Die Konfigurationsdatei kann in der Befehlszeile mit der Option
--config angegeben oder
config.yaml in das Arbeitsverzeichnis gestellt werden. Die Konfiguration ist optional, Sie müssen dort nach Bedarf klettern.
Mehr zur Konfiguration .
Testskript
Für mich selbst habe ich beschlossen, Skripte in Dateien mit der Erweiterung .pos zu schreiben, damit Sie Code-Hervorhebungseinstellungen in der IDE mit einer Erweiterungserweiterung vornehmen können. Dem Dolmetscher ist die Erweiterung jedoch völlig gleichgültig.
Hier ist ein einfaches Skriptbeispiel für ein imaginäres Forum, in dem der Test zum Erstellen und Lesen eines Beitrags durch verschiedene Benutzer durchgeführt wird.
require "./globals.pos"
Ja, ohne Hintergrundbeleuchtung sieht es nicht sehr gut aus.
Jede Zeile des Skripts beginnt mit einem Operator oder ist ein Aufruf einer API-Methode. Wenn der Name der API-Methode plötzlich mit einem Wort beginnt, das einem der Operatoren entspricht, können Sie das Symbol "
> " verwenden:
>Include $user to group $userGroup
Operatoren unterscheiden nicht zwischen Groß- und Kleinschreibung. assert, ASSERT oder aSsErT (aber warum so schreiben?) funktionieren.
Jeder Anweisungs- oder API-Methodenaufruf muss in einer separaten Zeile stehen. Zeilenumbruch ist aber auch möglich, wenn das letzte Zeichen der Zeichenfolge
\ ist (Hallo, Python).
Uninteressante Details zu Zeilenumbrüchen und EinrückungenWenn in einem Kommentar ein Zeilenumbruch verwendet wird, wird die nächste Zeile ebenfalls als Teil des Kommentars betrachtet. Wenn Sie Zeilen in Blöcke
einschließen (
Testfall ,
wenn ,
während , für
jeden ), ist es wichtig,
einzurücken, damit die nächste Zeile in denselben Block fällt.
var $n = 20 var $i = 2 var $fib1 = 1; \ $fib2 = 1 while $i <= $n var $fib_sum = \ $fib2 + $fib1 print toString($i) + " :" + \ toString($fib_sum) var $fib1 = $fib2 var $fib2 = $fib_sum var $i = $i + 1
Bei der Ausführung von
Blockanweisungen (
Testfall ,
if ,
while ,
foreach ) wird ein Block durch den Einzug seiner Zeilen bestimmt. Einrückung wird als Anzahl der Leerzeichen am Zeilenanfang gezählt. Sowohl Leerzeichen als auch Tabulatoren zählen als ein Zeichen, Tabulatoren werden jedoch normalerweise in Editoren als mehrere Leerzeichen angezeigt. Um Verwirrung zu vermeiden, ist es daher besser, entweder Tabulatoren oder Leerzeichen zu verwenden, jedoch nicht alle zusammen.
Vollständige Liste der Operatoren
require fileName -
hängt die Datei an die Stelle an, an der der Operator aufgerufen wird. Die angehängte Datei beginnt sofort in der ersten Zeile. Nach Abschluss kehrt der Interpreter zur nächsten Zeile der Quelldatei zurück. Wenn die angeforderte Datei nicht lesbar ist, wird ein Fehler generiert. Der relative Pfad wird aus dem Arbeitsverzeichnis berechnet.
include fileMask - ähnlich wie erforderlich, aber wenn die angeforderte Datei nicht lesbar ist, tritt kein Fehler auf. Dies ist beispielsweise praktisch, um Einstellungen für verschiedene Testumgebungen zu erstellen. Darüber hinaus kann include alle Dateien gleichzeitig über eine Maske verbinden. So können Sie beispielsweise ganze Verzeichnisse von Dateien herunterladen, die Testfälle enthalten. Gleichzeitig kann jedoch nicht garantiert werden, dass Dateien heruntergeladen werden.
var $ variable1 = expression1 ; $ variable2 = expression2 ; ...; $ variableN = expressionN - Weist Variablen Werte zu. Wenn die Variable noch nicht vorhanden ist, wird sie im aktuellen Kontext erstellt.
let $ variable1 = expression1 ; $ variable2 = expression2 ; ...; $ variableN = expressionN - Weist Variablen Werte zu. Wenn sich die Variable nicht im aktuellen Kontext befindet, wird versucht, die Variable im globalen Kontext zu erstellen oder zu ändern.
const $ const1 = expression1 ; $ const2 = expression2 ; ...; $ constN = expressionN -
Legt den Wert der Konstanten im aktuellen Kontext fest. Der Unterschied zwischen Konstanten und Variablen besteht nur darin, dass sie nicht geändert werden können. Wenn Sie versuchen, einer Konstanten einen Wert zuzuweisen, wird nach der Deklaration eine Warnung ausgegeben. Wenn bereits eine Variable mit demselben Namen vorhanden ist, wird beim Versuch, sie als Konstante zu deklarieren, ein Fehler generiert. Ansonsten gilt alles, was für Variablen gilt, auch für Konstanten.
importiere $ variable1 ; $ variable2 ; ...; $ variableN - kopiert Variablen aus dem globalen in den aktuellen Kontext. Dies kann nützlich sein, wenn Sie den Wert einer globalen Variablen bearbeiten, aber nicht ändern müssen.
testcase testCaseName - kündigt einen Testfall an, der dann als Einheit mit der
run- Anweisung aufgerufen werden kann.
Lesen Sie später in diesem Artikel mehr über Testfälle .
Ausdruck bestätigen - Überprüfen Sie, ob der
Ausdruck wahr ist. Andernfalls drucken Sie einen Bericht über den fehlgeschlagenen Test.
Der Ausdruck " must" ist derselbe wie "
assert" . Nur wenn der Test fehlschlägt, wird der aktuelle Testfall gestoppt. Außerhalb des Kontextes des Testfalls wird das Skript vollständig beendet. Es kann verwendet werden, wenn ein Fehler abgefangen wird, bei dem weitere Überprüfungen keinen Sinn ergeben.
testCaseName ausführen - Führen Sie den angegebenen Testfall zur Ausführung aus.
Wenn Sie ohne Angabe des Namens des Testfalls ausgeführt werden, werden alle deklarierten Testfälle gestartet, für die keine Argumente in der Reihenfolge ihrer Deklaration erforderlich sind.
while- Ausdruck - Eine Schleife, während
expression true ist, führt Anweisungen mit eingerückten Zeilen mehr als
while aus .
foreach $ array ; $ element - Schleife durch das Array, der Schleifenkörper wird für jedes nächste Element des Arrays ausgeführt. Es ist auch möglich, den Schlüssel für
jedes $ -Array zu erhalten . $ key ; $ element . Die Variablen
$ key und
$ element werden im aktuellen Kontext erstellt / überschrieben.
if expression - Wenn
expression true ist, werden Anweisungen mit eingerückten Zeilen mehr als
if ausgeführtprint expression1 ; expression2 ; ... expressionN - Gibt den Wert von
expressionM in stdout aus. Es kann zum Debuggen verwendet werden, es funktioniert nur mit der Stufe "Gesprächigkeit" -
Verbosität = 1 oder
-v und mehr.
Schlafausdruck - Pause für eine bestimmte Zahl, optional eine Ganzzahl, Sekunden. Manchmal müssen Sie der getesteten API eine Pause geben.
Pausenausdruck - nicht im interaktiven Modus (Befehlszeilenoption
-n ) ähnelt dem
Ruhezustand .
Der Ausdruck ist optional. In diesem Fall erfolgt keine Pause. Halten Sie im interaktiven Modus an, bevor Sie die Eingabetaste drücken.
Abbrechen - Test beenden. Der Dolmetscher beendet die Arbeit und erstellt Berichte.
API-Testmethoden
Dies ist eigentlich das, was Sie testen müssen - rufen Sie mit bestimmten Parametern auf und prüfen Sie, ob die Antwort den Erwartungen entspricht.
API-Methoden werden im YAML-Format beschrieben. Standardmäßig sollten sich Beschreibungen in der Datei
endpoints.yaml des aktuellen Verzeichnisses und / oder in den Dateien
* .yaml im Unterverzeichnis
./endpoints befinden . Vor dem Testen versucht der Interpreter, alle diese Dateien gleichzeitig zu lesen.
Beispiel
endpoints.yaml Struktur:
Auth $user: method: "POST" url: $domain + "/login" headers: Content-Type: "application/json" format: "json" data: login: $user.login password: $user.password after: - assert $response.code == 200 - let $user.auth_token = $response.body.auth_token Create post $post by $user: method: "POST" url: $domain + "/posts" format: "json" data: $post headers: auth: "Bearer " + $user.auth_token content-type: "application/json" after: - assert $response.code == 201 Read post $postId by $user: method: "GET" url: $domain + "/posts/" + $postId headers: auth: "Bearer " + $user.auth_token content-type: "application/json" after: - assert ($response.code == 200) || ($response.code == 404) Create comment $comment on $post by $user: method: "POST" url: $domain + "/comments/create/" + $post.id format: "json" data: $comment headers: auth: "Bearer " + $user.auth_token content-type: "application/json" after: - assert $response.code == 201
Der Name der API-Methode (die oberste Ebene der YAML-Struktur), über die sie aufgerufen werden kann, ist eine Zeichenfolge in einem nahezu beliebigen Format.
Argumente können an einer beliebigen Stelle im Namen angegeben werden. Sie sollten durch Leerzeichen vom Rest der Wörter getrennt sein. Zum Beispiel
$ comment ,
$ post und
$ user in der letzten Methode.
Außerdem können Sie an einer beliebigen Stelle im Namen optionale Methodenwerte in doppelten geschweiften Klammern angeben.
Get comments of $post {{$page=1; $perPage=$defaultGlobalPageSize}}: method: "GET" url: $domain + "/comments/" + $post.id query: page: $page per_page: $perPage headers: auth: "Bearer " + $user.auth_token content-type: "application/json" after: - assert $response.code == 200
In Ausdrücken, die optionale Werte angeben, sind globale Kontextvariablen verfügbar.
Optionale Werte können nützlich sein, damit Sie sie nicht jedes Mal angeben, wenn Sie die API-Methode aufrufen. Wenn die Seitengröße nur an einer Stelle geändert werden muss, warum an allen anderen Stellen angeben? Beispiele für Aufrufe dieser Methode:
Get comments of $newPost // $page $perPage Get comments of $newPost {{$page=$currentPage+1}} Get comments of {$newPost} {{$perPage=10;$page=100}}
Der Rest der verwendeten Variablen (
$ domain im obigen Beispiel) wird aus dem globalen Kontext übernommen. Ich werde Ihnen später mehr
über Kontexte erzählen.
Es erscheint mir zweckmäßig, API-Methoden lesbare Namen in einer natürlichen Sprache zu geben, dann ist das Testskript leichter zu lesen. Bei Namen wird die Groß- und Kleinschreibung nicht berücksichtigt, d. H. Die
Auth $ User- Methode kann als
auth $ User und als
AUTH $ User bezeichnet werden . Bei Variablennamen wird jedoch zwischen Groß- und Kleinschreibung unterschieden. Weitere
Informationen zu Variablen finden Sie weiter unten.
Wichtiger Hinweis. Mit dem YAML-Format können Sie Zeichenfolgen nicht in Anführungszeichen setzen. Für den Interpreter ist eine Zeichenfolge ohne Anführungszeichen ein Ausdruck, der ausgewertet werden muss. Wenn Sie beispielsweise ein
url: http://example.com/login
Feld deklarieren
url: http://example.com/login
, wird während der Ausführung ein Syntaxfehler angezeigt. Daher ist es korrekt:
url: "http://example.com/login"
oder
url: "http://"+$domain+"/login"
API-Methode Beschreibung Felder
Methode - erforderliche HTTP-Methode
URL - die tatsächliche URL, die erforderlich ist
Header - Liste der HTTP-Header, optional
Cookies - optionale Cookie-Liste
auth - Daten für die HTTP-Authentifizierung, optional
auth: login: $login password: $password type: "basic"
Abfrage - eine Liste von URL-Parametern, optional
Format - einer der Werte:
- keine - die Anfrage hat keinen Körper
- json - an JSON senden
- raw - sende den String "wie er ist"
- form - im Format application / x-www-form-urlencoded
- mehrteilig - im mehrteiligen / Formulardatenformat
Optional, Standard
keineDaten - Anforderungshauptteil, wird in dem im Format angegebenen
Format gesendet, optional
In den Dateifeldern angegebene
Dateien müssen lesbar sein. Wenn eine URL angegeben ist, muss
allow_url_fopen in der php.ini aktiviert sein
before - Anweisungen, die vor der HTTP-Anforderung ausgeführt werden, optional
after - Anweisungen, die nach der HTTP-Anforderung ausgeführt werden, optional
Die Idee der
Vorher- und
Nachher- Blöcke bei der Durchführung von Überprüfungen oder der Verarbeitung von Daten, die jedes Mal vor oder nach der Ausführung der HTTP-Anforderung benötigt werden, wird weniger von den Testanforderungen als vielmehr von der Geschäftslogik bestimmt. Kopieren Sie beispielsweise das ausgestellte Autorisierungstoken in das Feld der $ user-Struktur, um alle nachfolgenden API-Methoden im Namen dieses Benutzers aufzurufen. Oder um den HTTP-Status der Antwort zu überprüfen, um ihn nicht jedes Mal nach einem Aufruf im Skript zu überprüfen.
API-Methodenaufruf
Um die API-Methode im Skript aufzurufen, müssen Sie gegebenenfalls ihren Namen und ihre Parameter angeben. Hier ist ein Beispiel für den Aufruf der letzten API-Methode aus der obigen Beschreibung:
Create comment $comments.1 on {$newPost} by {$postAuthor}
Wenn der Parameter in geschweiften Klammern steht, wird er als Wert übergeben. Auf diese Weise können Sie einen beliebigen Ausdruck übergeben. Wenn Sie einen Parameter ohne geschweifte Klammern angeben, wird er als Referenz übergeben. Dies können nur Variablen und statische Zugriffe auf Array-Elemente sein (über einen Punkt, nicht jedoch über Klammern []).
Create comment {$comments[$i]} on $posts.0 by $users.1 Read post {123} by $user Get comments of $users.1.id {{$page = 2}}
Jedes Mal, wenn die API-Methode im Kontext des Aufrufs selbst (in den Listen der
Vorher- und
Nachher- Anweisungen) und in dem Kontext, in dem sie aufgerufen wurde, aufgerufen wird, werden die Variablen
$ request und
$ response erstellt . Dies sind reservierte Namen, ich empfehle nicht, sie für andere Zwecke zu verwenden.
$ request ist sowohl
vor als auch
nach Blöcken verfügbar, und
$ response ist nur
nach ,
bevor sein Wert
Null wird . Im aufrufenden Kontext sind diese Variablen bis zum nächsten API-Methodenaufruf verfügbar, wo sie neu initialisiert werden.
$ Struktur anfordern
$ request.method - String - HTTP-Methode
$ request.url - String - die angeforderte URL
$ request.query - Array - Eine Liste der GET-Parameter
$ request.headers - Array - Liste der Anforderungsheader
$ request.cookies - Array -
Cookie- Liste
$ reuqest.auth -
Array- oder
Nulldaten für die HTTP-Authentifizierung
$ request.format - String - Anforderungsdatenformat
$ request.data -
Geben Sie any ein - was im
Datenfeld berechnet wurde
$ Antwortstruktur
$ response.network - Boolean - false, wenn der Fehler auf Netzwerkebene unterhalb von HTTP lag
$ response.code - Number oder Null - Antwortcode, z. B. 200 oder 404
$ response.status - String oder Null - Antwortstatus, z. B. "204 No Content" oder "401 Unauthorized"
$ response.headers - Array - Liste der Antwortheader, Headernamen sind Kleinbuchstaben
$ response.cookies - Array - Cookies-Liste
$ response.body - ein beliebiger Typ - Antworttext, der als JSON verarbeitet wird. Wenn beim Parsen ein Fehler aufgetreten ist, ist das
body- Element überhaupt nicht
vorhanden :
@response.body == null
(
Informationen zum Überprüfen der Existenz von Variablen )
$ response.raw - String oder Null - der rohe Antworttext
$ response.duration - Typ Number - Anforderungsdauer in Sekunden
Generierung von Modellen und Testdaten
Generatoren werden verwendet, um Modelle zu beschreiben und daraus Testdaten zu generieren. Beschreibungen im YAML-Format sollten in der Datei
generators.yaml im Arbeitsverzeichnis und / oder in den Dateien
* .yaml im Unterverzeichnis
./generators enthalten sein .
User: body: login: Faker\login() name: Faker\name() email: Faker\email() password: Faker\text(16) child: Child() birthday: dateFormat(Faker\datetime(), "U") settings: notifications_enabled: Faker\boolean() Child: body: name: Faker\name() gender: Faker\integer(1, 2) age: Faker\integer(0, 18) Comment($user): body: content: "Hi! I'm " + $user.name tags: - "tag1" - "tag2"
Im obigen Beispiel werden die drei Generatoren
User () ,
Child () und
Comment () deklariert. In diesem Fall hat letzterer das Argument
$ user und kann diese Daten beim Generieren verwenden. Argumente an Generatoren werden immer als Wert übergeben. Darüber hinaus werden im Beispiel mehrere weitere integrierte Funktionen verwendet:
Faker \ name () ,
Faker \ email () ,
dateFormat () usw.
Abschnitt über eingebaute Funktionen .
Wenn Sie den
User () - Generator aus dem obigen Beispiel aufrufen, wird eine Struktur generiert, die in JSON folgendermaßen aussieht:
{ "login": "fgadrkq", "name": "Lucy Cechtelar", "email": "tkshlerin@collins.com", "password": "gbnaueyaaf", "child": { "name": "Adaline Reichel", "gender": 2, "age": 12 }, "birthday": 318038400, "settings": { "notifications_enabled": true } }
Der Wert des
untergeordneten Felds ist das Ergebnis des
Child () - Generators.
Wie in der Beschreibung der API-Methoden werden alle Zeichenfolgen, die nicht in Anführungszeichen gesetzt sind, als auszuwertende Ausdrücke behandelt. Dies kann nicht nur ein Aufruf eines anderen Generators sein, sondern auch ein beliebiger Ausdruck. Beispielsweise repräsentiert das
Inhaltsfeld im
Kommentargenerator ($ user) die Verkettung der Zeichenfolge Hi! Ich bin und der Name wurde an
$ user übergebenDie Namen der Generatoren unterscheiden nicht zwischen Groß- und Kleinschreibung und müssen mit einem lateinischen Buchstaben beginnen. Sie können lateinische Buchstaben, Zahlen, Unterstriche und Backslashes enthalten.
Da die Syntax zum Aufrufen von Generatoren und integrierten Funktionen identisch ist, haben sie einen gemeinsamen Namespace. Konventionell schlage ich vor, einen Backslash als Trennzeichen für die Angabe eines "Anbieters" oder einer Bibliothek integrierter Funktionen zu verwenden, z. B. der Funktionen Faker \ Something (), basierend auf der Bibliothek
github.com/fzaninotto/Faker .
Die Nuancen der Verwendung von Generatoren kann man nicht lesenMit Generatoren können Sie Datenstrukturen erstellen:
# Userredentials $user Userredentials($user): body: login: $user.email password: $user.password # . , GlobalSearchResult($posts, $comments, $users): body: posts: title: " " list: $posts comments: title: " " list: $comments users: title: " " list: $users
GlobalSearchResult sind keine Testdaten, die in der Anforderung an die API-Methode gesendet werden, sondern ein Antwortmodell, das anhand der von der API gesendeten Daten überprüft werden kann, z. B. mithilfe der
ähnlichen () oder
identischen () Funktionen.
Der Generator kann die im
Körper erhaltene Struktur mithilfe von Strukturen ändern, die in den Feldern
Ersetzen und
Entfernen berechnet wurden. Ich zeige Ihnen ein Beispiel.
Angenommen, Sie haben bereits einen
User () - Generator, der die richtige Datenstruktur für den Benutzer erstellt. Jetzt müssen Sie überprüfen, wie die API reagiert, wenn Sie falsche Daten angeben. Sie können auf zwei Arten vorgehen:
- Erstellen Sie einen "falschen" Benutzergenerator von Grund auf neu. Aber dann erhalten wir eine Codeduplizierung, und später, wenn Sie dem Benutzer beispielsweise ein neues Feld gemäß den Anforderungen der Geschäftslogik hinzufügen, müssen Sie an zwei Stellen Änderungen vornehmen. TROCKEN!
- Sie können es von der vorhandenen User () - Struktur „erben“, indem Sie es im Hauptteil festlegen . Legen Sie beim Ersetzen und Entfernen die Felder fest, die hinzugefügt / geändert und gelöscht werden sollen.
# , , # InvalidUser($user): body: $user replace: email: Faker\String(6, 15) # password: Faker\String(1, 5) # new_field: " , " remove: name: size($user.name) < 10 # , 10 # , # InvalidNewUser: body: User() replace: login: "!@#$%^&*" # remove: about: true settings: notifications: 100500 # , # true
Wenn der Generator arbeitet, wird zuerst die Datenstruktur im
Body berechnet, dann überschrieben und mit Elementen aus
replace ergänzt. Anschließend werden die in
remove angegebenen Felder gelöscht, wenn ihr Wert
true entspricht . Wenn das Ergebnis der Berechnung des
Körpers ,
Ersetzens oder
Entfernens kein Array ist, gibt es keinen Fehler, aber es hat auch keinen Sinn, da es keine Felder gibt, die ersetzt und gelöscht werden könnten.
Eingebaute Funktionen
Vollständige Liste der integrierten Funktionen . Zum Beispiel werde ich nur einige von ihnen geben.
Nach dem Funktionsnamen und der Argumentliste wird der
Typ des Rückgabewerts angegeben, sofern einer definiert ist.
Operationen mit Variablen:
ähnlich ($ var, $ sample, $ checkTypes)
Boolean - gibt
true zurück
, wenn die Argumente vom gleichen Typ sind. Wenn
$ var ein Array ist, sollten alle Zeichenfolgenschlüssel in
$ sample in
$ var sein , wenn
$ checkTypes ist true, dann müssen die Typen der entsprechenden Elemente übereinstimmen. Mit anderen Worten, die Elemente des
$ var- Arrays sind eine Teilmenge der Elemente von
$ sample .
identisch ($ var, $ sample, $ checkTypes) Boolean ist ein Analogon von ähnlich () , mit der zusätzlichen Umkehrung, dass bei Arrays alle Zeichenfolgenschlüssel in $ var auch in $ sample enthalten sein sollten . Mit anderen Worten, die Elemente des $ var- Arrays sind bis zum Elementtyp gleich den Elementen des $ sample- Arrays .max ($ var1, $ var2, ... $ varN) - das Maximum der übergebenen Werte (wenn sie verglichen werden können).min ($ var1, $ var2, ... $ varN) - das Minimum der übergebenen Werte.if ($ condition, $ var1, $ var2) - Wenn $ condition == true ist, wird $ var1 zurückgegeben, andernfalls $ var2. Ersetzen des Coaching-Operators (Hallo MySQL).Wahl ($ condition1, $ var1, $ condition2, $ var2, ..., $ conditionN, $ varN) - Gibt das erste angetroffene $ varK zurück, wenn $ conditionK == true ist.Arbeit mit Strings:
size ($ string) Number - Die Länge der Zeichenfolge in der UTF-8-Codierung.Regex ($ string, $ regex) Boolescher Wert - Überprüfen Sie den String auf regulären Ausdruck .regexMatch ($ string, $ regex) Array - gibt ein Array von Zeichenfolgen zurück - stimmt mit den regulären Gruppen $ regex überein .Array-Verarbeitung:
Array ($ var1, $ var2, ... $ varN) Array - Erstellt ein Array aus den übergebenen Elementen.size ($ array) Number - Die Anzahl der Elemente im Array.Schlüssel ($ array) Array - Eine Liste der Schlüssel im Array.Slice ($ Array, $ Offset, $ Länge) Array - Teil des Arrays von $ Offset der Länge $ Länge, ( mehr ).append ($ array, $ value) Array - Fügen Sie am Ende des Arrays ein Element hinzu.prepend ($ array, $ value) Array - Fügen Sie am Anfang des Arrays ein Element hinzu.Datumsverarbeitung:
dateFormat ($ date, $ format) String - Datumsformatierung ( mehr zu Formaten ).dateModify ($ date, $ format) Date - Ändert das Datum, was für die Verwendung mit relativen Formaten praktisch ist .Zufällige Testdatengenerierung:
Faker \ integer ($ min, $ max) Number - zufällige Ganzzahl von $ min bis $ max einschließlichFaker \ ipv4 () String - zufälliges IPv4Faker \ arrayElement ($ array) String - zufälliges Element aus dem ArrayFaker \ name () String - zufälliger NameFaker \ email () String - zufällige E-MailJetzt gibt es nicht mehr viele eingebaute Funktionen. Ich habe nur das hinzugefügt, was mir beim Testen als notwendig erscheint. Sie können in neuen Versionen nach Bedarf neue Funktionen hinzufügen. Und in Zukunft werde ich, wenn es gefragt ist, die Möglichkeit hinzufügen, dynamisch verbundene Funktionen zu erstellen, die als spezielle Klassen in PHP implementiert sind.Testfälle
Ein Testfall ist eine Folge von Anweisungen, die als Einheit aufgerufen werden können. Einige Analoga des Verfahrens in Programmiersprachen.Ein Testfall wird durch die Testfallanweisung erstellt , gefolgt vom Namen des Testfalls mit einer Syntax ähnlich den API-Methodennamen . Verschachtelte Testfälle sind verboten. testcase Registration $device
Die run- Anweisung kann einen separaten Testfall oder alle Testfälle aufrufen, für die keine Argumente erforderlich sind. run Get all users
Die Idee eines solchen Starts ist, dass Testfälle als separate unabhängige Tests eines Teils der Geschäftslogik und als Verfahren zur Vermeidung von Codeduplizierungen in komplexen Testszenarien verwendet werden können.Argumente werden in der vollständigen Analogie der Übergabe von Argumenten an API-Methoden als Referenz oder als Wert an den Testfall übergeben.Variablen und Bereiche
Variablennamen unterscheiden zwischen Groß- und Kleinschreibung und beginnen mit einem $ -Zeichen (ja, ja, ich bin ein pshpshnik).Wenn der Variable Typ des Array , dann Zugriff auf einzelne Felder oder Elemente des Wertes wird durch den Punkt erzeugt: $users.12.password
. Zwischen Punkten sind nur Zahlen oder lateinische Buchstaben, Unterstriche und Zahlen mit dem ersten lateinischen Buchstaben zulässig. Bei Feldnamen wird auch zwischen Groß- und Kleinschreibung unterschieden.Ein dynamischer Zugriff auf ein Array-Element ist möglich:$post.comments[$i + 1].content
Es gibt vier Arten von Kontexten - den Umfang der Variablen.Globaler Kontext - wird zu Beginn erstellt und enthält alle Variablen, die beim Ausführen von Anweisungen außerhalb von Testfällen und außerhalb von API-Methodenaufrufen deklariert wurden.Testfallkontext - Jedes Mal, wenn der Testfall mit der run- Anweisung ausgeführt wird, wird ein neuer erstellt .API-Methodenkontext - Wird erstellt, wenn die API-Methode aufgerufen wird und die in den Abschnitten vor und nach angegebenen Operatoren ausgeführt werden .Generator-Kontext- In den Generatoren gibt es keine Möglichkeit, neue Variablen zu erstellen oder vorhandene Variablen zu ändern. Daher sind globale Kontextvariablen und Argumente schreibgeschützt. Variablen werden immer als Wert an den Generatorkontext übergeben.Wichtiger Hinweis. In allen Kontexten sind globale Kontextvariablen verfügbar, wenn ihre Namensvetter nicht im aktuellen Kontext erstellt werden.Beispiele für Operatoren zum Arbeiten mit Variablen: const $a = 1 let $a = 2
let $a = 1; $a = $a + 1; $a = $a + 2 print $a
Testcase Context example changes $argument1, $argument2 and $argument3 var $a = "changed" let $b = "changed" const $c = "changed" import $i let $i = "changed" var $argument1 = "changed" var $argument2 = "changed" var $argument3 = "changed"
Geben Sie System and Operations ein
Es wird eine lockere dynamische Typisierung verwendet.Werte werden in Wrappern über den entsprechenden PHP-Typen gespeichert. Zum besseren Verständnis siehe das PHP-Typ-System. Bei der dynamischen Typkonvertierung habe ich etwas weniger Freiheit gemacht. Wenn Sie beispielsweise eine Zeichenfolge und eine Zahl hinzufügen "2" + 2
, wird ein Fehler generiert, und PHP führt die Addition leise durch. Vielleicht muss ich in Zukunft die Regeln für dynamisches Tippen überarbeiten, aber bisher habe ich versucht, ein Gleichgewicht zwischen dem Komfort und der Genauigkeit zu finden, die für zuverlässige Tests erforderlich sind.In PieceofScript verfügbare Datentypen:NummerIst eine Nummer. Aus Gründen der Einfachheit habe ich keine separaten Typen für Integer und Float erstellt. Der einzige signifikante Unterschied zwischen der Ganzzahl und den reellen Zahlen in PieceofScript besteht in der Verwendung eines Arrays als Schlüssel: Die reellen Zahlen werden auf ganze Zahlen gerundet.7 -42 3.14159
String - Zeichenfolgen in doppelten Anführungszeichen, die möglicherweise mit einem"I say \"Hello world!\""
Null- Schrägstrich versehen sind - festgelegt durch die Konstantenull
Boolean ohne Berücksichtigung der Groß- und Kleinschreibung - ist das Ergebnis von Booleschen Operationen und Vergleichsoperationen, die durch Konstanten ohnetrue false
Berücksichtigung der Groß- und Kleinschreibung festgelegt werden. Datum - Datum und Uhrzeit. "Unter der Haube" ist eine DateTime . Konstanten werden in einfachen Anführungszeichen in einem der Formate angegeben .'now', '2008-08-07 18:11:31', 'last day of next month'
Array ist ein Array, der einzige nicht skalare Typ. UmwickelnDas Array . Es gibt keine Literale dieses Typs, aber Arrays können das Ergebnis der Arbeit von Generatoren, integrierten Funktionen (z. B. array () - Hallo PHP 5.3 und niedriger) sein oder Sie können einfach auf die Variablenschlüssel zugreifen, die bei der Zuweisung dynamisch erstellt werden. let $a.1 = 100 let $i = 1 let $a[$i + 1] = 200 let $a.sum = $a.1 + $a.2 print " "; $a.sum // 300 var $b = array(true, 3, $a, "Hi") // [true, 3, {1: 100, 2: 200, "sum":300}, "Hi"]
Beim Zugriff auf eine nicht vorhandene Variable oder ein Array-Element wird das Testskript gestoppt und ein Fehler generiert. Wenn beim Ausführen von assert- oder must- Anweisungen auf eine nicht vorhandene Variable zugegriffen wird, tritt kein Fehler auf, die Prüfung wird jedoch als fehlgeschlagen betrachtet.Überprüfen der Existenz und des Typs einer Variablen
Wir müssen die Konstruktion der Überprüfung der Existenz und des Typs der Variablen @ separat erwähnen .Wenn der Name einer Variablen anstelle von $ die angeben @ , wird das Ergebnis dieses Entwurfs einer der folgenden sein:- eine Zeichenfolge mit dem Namen des Typs der Variablen oder dem Typ des Elements des Arrays, wenn Schlüssel verwendet wurden;
- null, wenn die Variable nicht in zugänglichen Kontexten gefunden wird oder das Element mit dem angegebenen Schlüssel im Array nicht vorhanden ist.
Dieses Design kann hilfreich sein, wenn Sie die Struktur von HTTP-Antworten überprüfen. var $a.string_field = "Hello World" var $a.number_field = 3.14 var $a.bool_field = true var $a.date_field = '+1 day' var $a.null_field = null var $a.array_field = array(1, "2") assert @a.string_field == "String" assert @a.number_field == "Number" assert @a.bool_field == "Boolean" assert @a.date_field == "Date" assert @a.null_field == "Null" assert @a.array_field == "Array" assert @a.array_field.0 == "Number" assert @a.array_field.1 == "String" assert @a.array_field.2 == null assert @notExistedVar == null
Oder in Konstruktionen wie: assert @comment.optional_field && $comment.optional_field > 20
Boolesche Operationen werden für den ersten Operanden optimiert. Wenn der erste Operand falsch ist, versucht die Operation && nicht einmal, den zweiten Operanden zu berechnen. Ähnliches gilt für || .
Speichern von Daten zwischen Läufen
Ich habe separate Skripte nicht nur zum Testen nach Abschluss der Aufgabe verwendet, sondern auch während der Entwicklung. Ich habe das Skript ergänzt und geändert, als ich die Funktion implementiert habe. Später wurde dieses Skript zur Grundlage für das Schreiben eines Testfalls, aber während der Entwicklung musste man immer wieder dieselben API-Aufrufe durchführen. Gleichzeitig war es jedes Mal im Skript eine lange Zeit, neue Entitäten von Grund auf neu zu erstellen (z. B. Benutzerregistrierung), Müll in der Datenbank zu erstellen und die Entwicklung in jeder Hinsicht zu beeinträchtigen. Aus diesem Grund habe ich beschlossen, die Möglichkeit hinzuzufügen, die Werte von Variablen zwischen den Starts im Schlüsselwertspeicher zu speichern und wiederherzustellen.Das Speichern wird durch die Befehlszeilenoption --storage aktiviert, mit der der Name der Speicherdatei festgelegt wird: pos.phar run ./start.pos --storage=storage.yaml
Die Daten werden im YAML-Format gespeichert, was das Lesen und Bearbeiten erleichtert.storage \ get (Zeichenfolge $ key, $ defaultValue, boolean $ saveValue = true) - Wenn der Schlüssel $ key nicht vorhanden ist oder die Speicherdatei nicht angegeben ist, wird $ defaultValue zurückgegeben. Andernfalls wird der gespeicherte Wert zurückgegeben. Wenn das Argument $ saveValue wahr ist und der Schlüssel $ key nicht gefunden wurde, wird dort $ defaultValue geschrieben.storage \ set (Zeichenfolge $ key, $ value) - speichert $ value mit dem Schlüssel $ key und gibt $ value zurück. Wenn die Speicherdatei nicht festgelegt wurde, wird einfach $ value zurückgegeben.storage \ key (Zeichenfolge $ regexp = null) Array- gibt ein Array aller verfügbaren Schlüssel zurück. Wenn das Argument $ regexp nicht null ist, werden die diesem regulären Ausdruck entsprechenden Schlüssel zurückgegeben. Wenn die Speicherdatei nicht festgelegt wurde, wird ein leeres Array zurückgegeben.Ausgabe an stdout
PieceofScript kann Berichte im JUnit- und HTML-Format generieren. Die erste wird für die Integration in CI / CD-Systeme benötigt, beispielsweise Jenkins. Die zweite Möglichkeit besteht darin, die Testergebnisse bequem selbst anzuzeigen, beispielsweise beim lokalen Testen. Berichtsdateien können beim Start festgelegt werden: pos.phar run ./start.pos --junit=junit_report.xml --html=report.html
Ein Beispiel für einen HTML-BerichtIn stdout werden verschiedene Informationen zur Arbeit des Interpreters angezeigt. Es gibt 5 Standardstufen für die Informationsausgabe. Alles, was auf derselben Ebene angezeigt wird, wird auch auf den anderen „gesprächigeren“ Ebenen angezeigt.Leise - Die leiseste Stufe wird mit der Befehlszeilenoption -q festgelegt .Auf dieser Ebene wird nichts an stdout ausgegeben, selbst kritische Interpreterfehler. Anhand des Rückkehrcodes ungleich Null können Sie jedoch erkennen, dass ein Fehler aufgetreten ist.Normal ist die Standardstufe, ohne Optionen anzugeben.Auf dieser Ebene werden im Interpreter Fehler generiert. Fehlgeschlagene Anfragen an die API - Methoden und der gescheiterten Test assert und ein Muss .Ausführlich - durch Option festgelegt-v .Auf dieser Ebene werden die Ergebnisse der Druckanweisung angezeigt .Sehr ausführlich - mit der Option -vv festgelegt .Auf dieser Ebene werden Dolmetscherwarnungen angezeigt.Debug - wird mit der Option -vvv festgelegt .Auf dieser Ebene werden alle ausgeführten Skriptzeilen angezeigt. Alle Anfragen und Antworten von API-Methoden, die Ergebnisse aller Assert und Muss- Checks .Beispiele
Das Sprichwort „Es ist besser, einmal zu sehen als hundertmal zu hören“ ist wahr und in der Interpretation „es ist besser, den Code einmal zu sehen, als seine Beschreibung hundertmal zu lesen“. Ich habe die Beispiele vorbereitet und in das Repository https://github.com/maximw/PosExamples gestellt .Virustotal
Virustotal.com - ein Dienst zum Überprüfen schädlicher Dateien und Links. API-Dokumentation . Tests werden für den öffentlichen Teil der API durchgeführt, mit Ausnahme von Kommentierungsmethoden, weil Ich möchte keine echte "Kampf" -API mit Testdaten verschmutzen.Um auf die API zuzugreifen, müssen Sie sich registrieren , den Schlüssel abrufen und zur Datei Virustotal / globals.pos hinzufügen .Ausführen von Tests: pos.phar run ./Virustotal/start.pos
Che gibt es für einen Exe-Shnik in einem Repository?Für Tests habe ich hiddeninput.exe aus der Konsolenkomponente des Symfony-Repositorys kopiert. Diese Datei kann gelöscht werden und für Tests kann jede andere Größe bis zu 32 MB verwendet werden.
Kloster
Pasebin-Analogon. API-Dokumentation .Um auf die API zuzugreifen, die Sie registrieren müssen , holen Sie sich den Schlüssel und fügen Sie ihn der Datei Pastery / globals.pos hinzu .Ausführen von Tests: pos.phar run ./Pastery/start.pos
Es ist bemerkenswert, dass bei diesen Tests ein Fehler in der Begrenzung der Anzahl der Ansichten gefunden wurde. Es wurde bereits von den Entwicklern von Pastery behoben.Der Rick und Morty
Ich denke, diese Zeichentrickserie ist vielen bekannt und wird von vielen geliebt. API-Dokumentation . API besteht aus drei fast identischen Abschnitten Charakter, Ort und Episode. Daher sind die Szenarien fast gleich, und nur die Testfälle zu betrachten, ist nur einer der Abschnitte.Ausführen von Tests: pos.phar run ./RickAndMorty/20MinutesTest.pos
Wenn Sie eine öffentliche API kennen, deren Test auf diese Weise interessant wäre, schreiben Sie bitte eine persönliche E-Mail.Gegebenenfalls Kommentare und Pläne für die Zukunft
0) Ich habe eine Liste kleiner und großer Verbesserungen, die ich noch nicht benötige, die aber nützlich sein können.Liste anzeigen- Fügen Sie einen Bericht über die Arbeit im Json-Format mit der Möglichkeit des Überschreibens nach mehreren Skriptläufen hinzu
- body replace remove
- , YAML
- HTTP- ,
- , run . .
- HTML- stdout, -vvv
- https
- application/x-www-form-urlencoded CURLFile . Guzzle 6,
- « »,
- API,
- HTML-, bootstrap- « », .
1) Für die Validierung von Modellen in API-Antworten mit Generatoren gibt es bisher nur zwei Funktionen - ähnlich () und identisch () . Die Validierung mit ihnen ist zu "ungeschickt". Natürlich ist es bereits möglich, die Antworten "von Hand" zu validieren, und in einigen Fällen ist dies auf keine andere Weise möglich, aber ich möchte es bequemer machen und nach Möglichkeit vermeiden, die Antwort manuell zu überprüfen. Es gibt einige Ideen, wie Sie Modelle mit derselben Modellbeschreibung generieren und validieren können, um Doppelarbeit zu vermeiden. Bisher wurden diese Ideen jedoch nicht so weit entwickelt, dass Sie sie in Code implementieren können.2) Ich denke, dass Gerüste für API-Methoden, die auf Beschreibungen in OpenAPI ( Swagger ), RAML , Sammlungen basieren , sehr nützlich sein werdenPostbote . Aber das ist eine Menge Arbeit, die es wert ist, sich hinzusetzen, wenn PieceofScript es wert ist.3) Es wäre schön, Plugins für einige IDEs mit Code-Hervorhebung und automatischer Vervollständigung zu erstellen. Die automatische Vervollständigung von Testfallnamen, API-Methoden, Operatoren und Variablen wäre einfach archi-bequem. Aber er hat noch nicht in diese Richtung "gegraben". Grundlegendes zum Erstellen von Hervorhebungen für das Sublime Text and Language Server-Protokoll . Ich würde mich freuen, wenn es Gleichgesinnte gibt, die sich bereits mit solchen Dingen auskennen.4) Ich weiß nicht, welche Priorität die Fähigkeit haben soll, dynamisch verbundene Funktionen zu erstellenin PHP implementiert. Einerseits ist dort alles einfach, es reicht aus, sich mit dem automatischen Laden zu befassen und eine Spezifikation der verwendeten Klassen und Namespaces vorzunehmen. Andererseits verursachen komplexe Funktionen mit ihren Abhängigkeiten zwangsläufig einen Namespace-Konflikt zwischen Abhängigkeiten (im schlimmsten Fall unterschiedliche Versionen). Es gibt auch etwas zu denken.5) Gute Testsysteme führen parallel unabhängige Tests durch. Dies kann nun erreicht werden, indem der Interpreter mehrmals mit verschiedenen Startdateien gestartet wird, wobei verschiedene Testfälle verbunden sind. Aber ich denke, wir müssen dies in den Interpreter selbst einbetten, um automatisch zu erkennen, was parallel gestartet werden kann.PS Einerseits, da dies mein "Handwerk" ist, wäre es logisch, einen Beitrag in den Hub "Ich bin PR" zu setzen. Auf der anderen Seite mache ich keine PR, verfolge keinen kommerziellen Gewinn, nur ein Instrument, das ich für mich selbst gemacht habe. Ich habe mich entschlossen, es zu "kämmen" und öffentlich zu machen.