Hallo allerseits! Mein Name ist Pasha und ich bin QS-Ingenieur für das Auftragsabwicklungsteam bei Lamoda. Ich habe kürzlich beim PHP Badoo Meetup gesprochen. Heute möchte ich eine Abschrift meines Berichts vorlegen.
Wir werden über Codeception sprechen, darüber, wie wir es in Lamoda verwenden und wie man Tests darauf schreibt.
Lamoda hat viele Dienstleistungen. Es gibt Client-Services, die direkt mit unseren Benutzern, mit Benutzern der Website und mobilen Anwendungen interagieren. Wir werden nicht über sie sprechen. Und es gibt das, was unser Unternehmen Deep Backends nennt - dies sind unsere Back-Office-Systeme, die unsere Geschäftsprozesse automatisieren. Dazu gehören Lieferung, Lagerung, Automatisierung von Fotostudios und ein Callcenter. Die meisten dieser Dienste werden in PHP entwickelt.
Kurz gesagt, dies ist PHP + Symfony. Hier und da gibt es alte Projekte auf Zend'e. PostgreSQL und MySQL werden als Datenbanken verwendet, und Rabbit oder Kafka werden als Messagingsysteme verwendet.
Warum PHP-Backends?Da sie normalerweise eine verzweigte API haben - es ist entweder REST, an einigen Stellen gibt es ein bisschen SOAP. Wenn sie eine Benutzeroberfläche haben, handelt es sich bei dieser Benutzeroberfläche eher um eine zusätzliche Benutzeroberfläche, die von unseren internen Benutzern verwendet wird.
Warum brauchen wir Autotests bei Lamoda?Als ich zu Lamoda kam, gab es im Allgemeinen einen solchen Slogan: „Lassen Sie uns die manuelle Regression loswerden“. Wir werden keine Regression manuell testen. Und wir haben an dieser Aufgabe gearbeitet. Tatsächlich ist dies einer der Hauptgründe, warum wir Autotests benötigen, um die Regression nicht von Hand voranzutreiben. Warum brauchen wir das? Recht auf schnelle Freigabe. Damit wir unsere Veröffentlichungen schmerzlos und sehr schnell herausbringen können und gleichzeitig eine Art Raster aus Selbsttests haben, die sie uns sagen, ob gut oder schlecht. Dies sind wahrscheinlich die wichtigsten Ziele. Aber es gibt ein paar Hilfsmittel, über die ich auch sagen möchte.
Warum brauchen wir Autotests?
- Testen Sie die Regression nicht mit Ihren Händen
- Schnellspanner
- Als Dokumentation verwenden
- Beschleunigen Sie die Aufnahme neuer Mitarbeiter
- Autotests werden (in einigen Fällen) bequem als Dokumentation verwendet. Manchmal ist es einfacher, in die Tests einzusteigen, zu sehen, welche Fälle behandelt werden, wie sie funktionieren, zu verstehen, wie diese oder jene Funktionalität funktioniert, und den Einstieg neuer Mitarbeiter - sowohl Entwickler als auch Tester - in ein neues Projekt zu beschleunigen. Wenn Sie sich hinsetzen, um Autotests zu schreiben, wird sofort klar, wie das System funktioniert.
Ok, ich habe darüber gesprochen, warum wir Autotests brauchen. Lassen Sie uns nun darüber sprechen, welche Tests wir in Lamoda schreiben.

Dies ist eine ziemlich standardmäßige Testpyramide, von Unit-Tests bis zu E2E-Tests, bei denen einige Geschäftsketten bereits getestet wurden. Ich werde nicht über die unteren beiden Ebenen sprechen, es ist nicht umsonst, dass sie in solch weißer Farbe übermalt sind. Dies sind Tests für den Code selbst, die von unseren Entwicklern geschrieben wurden. In extremen Fällen kann der Tester in die Pull-Anfrage gehen, sich den Code ansehen und sagen: "Nun, etwas ist hier nicht genug, lassen Sie uns etwas anderes behandeln." Damit ist die Arbeit des Testers für diese Tests abgeschlossen.
Wir werden über die oben genannten Ebenen sprechen, die von Entwicklern und Testern geschrieben wurden. Beginnen wir mit Systemtests. Dies sind Tests, die die API (REST oder SOAP) testen, interne Systemlogik, verschiedene Befehle testen, Warteschlangen in Rabbit analysieren oder mit externen Systemen austauschen. Diese Tests sind in der Regel ziemlich atomar. Sie überprüfen keine Kette, sondern eine Aktion. Zum Beispiel eine API-Methode oder ein Befehl. Und sie prüfen so viele Fälle wie möglich, sowohl positive als auch negative.
Fahren Sie fort, E2E-Tests. Ich habe sie in zwei Teile geteilt. Wir haben Tests, die eine Reihe von UI und Backend testen. Und es gibt Tests, die wir Flusstests nennen. Sie testen die Kette - das Leben eines Objekts von Anfang bis Ende.
Zum Beispiel haben wir ein System zur Verwaltung der Verarbeitung unserer Bestellungen. Innerhalb eines solchen Systems kann es einen Test geben - einen Auftrag von der Erstellung bis zur Lieferung, dh das Durchlaufen aller Status. Bei solchen Tests ist es dann sehr einfach und unkompliziert zu beobachten, wie das System funktioniert. Sie sehen sofort den gesamten Fluss bestimmter Objekte, mit welchen externen Systemen all dies interagiert, welche Befehle dafür verwendet werden.
Da diese Benutzeroberfläche von internen Benutzern verwendet wird, ist der browserübergreifende Zugriff für uns nicht wichtig. Wir führen diese Tests nicht in Farmen durch, es reicht aus, wenn wir einen Browser einchecken, und manchmal müssen wir nicht einmal einen Browser verwenden.
"Warum haben wir Codeception für die Testautomatisierung gewählt?" - Sie fragen wahrscheinlich.
Um ehrlich zu sein, habe ich keine Antwort auf diese Frage. Als ich zu Lamoda kam, wurde Codeception bereits als Standard für das Schreiben von Autotests ausgewählt, und ich bin tatsächlich darauf gestoßen. Aber nachdem ich einige Zeit mit diesem Framework gearbeitet hatte, verstand ich immer noch, warum Codeception. Das möchte ich mit Ihnen teilen.
Warum Codezeption?- Sie können dieselben Tests jeder Art (Einheit, Funktion, Akzeptanz) schreiben und ausführen.
- Viele Rechen wurden bereits gelöst, viele Module wurden bereits geschrieben.
- In allen Projekten sehen die Tests trotz leicht unterschiedlicher Anforderungen gleich aus.
- Das Konzept der Codezeption schlägt vor, dass Sie alle Tests zu diesem Framework schreiben: Einheit, Integration, Funktion, Akzeptanz. Und zumindest Sie, sie werden gleichermaßen gestartet.
- Codeception ist ein ausreichend leistungsfähiger Prozessor, in dem viele Probleme, viele Fragen und viele Aufgaben für Tests bereits gelöst wurden. Wenn etwas nicht entschieden wird, werden Sie höchstwahrscheinlich etwas von außen finden - ein Add-On für eine bestimmte Arbeit. Sie müssen keine Test-Wrapper für Datenbanken schreiben, für etwas anderes. Nehmen Sie einfach Module, verbinden Sie sie mit Codeception und arbeiten Sie mit ihnen.
- Nun, ein solches Plus (wahrscheinlich ist es besser für große Unternehmen geeignet, wenn Sie viele Projekte und Dienstleistungen haben) - in allen Projekten sehen die Tests plus oder minus gleich aus. Das ist sehr cool.
Ich werde kurz sagen, wie Codeception ist, da viele damit gearbeitet haben.

Codeception arbeitet an einem Akteurmodell. Nachdem Sie es in das Projekt gezogen und initialisiert haben, wird eine solche Struktur generiert.
Wir haben yml-Dateien, hier unten -
function.suite.yml ,
Integration.suit.yml ,
unit.suite.yml . Sie erstellen die Konfiguration Ihrer Tests. Es gibt Väter für jede Art von Test, wo diese Tests sind, gibt es 3 Hilfsväter:
_
Daten - für Testdaten;
_
output - wo Berichte abgelegt werden (xml, html);
_
support - Hier werden einige Hilfshilfen, Funktionen und alles, was Sie schreiben, für Ihre Tests verwendet.
Zunächst werde ich Ihnen sagen, was wir aus Codeception übernommen haben, und es sofort verwenden, ohne etwas zu ändern, ohne zusätzliche Aufgaben oder Probleme zu lösen.
Standardmodule- Phpbrowser
- RUHE
- Db
- Cli
- AMQP
Das erste derartige Modul ist PhpBrowser. Dieses Modul ist ein Wrapper über Guzzle, mit dem Sie mit Ihrer Anwendung interagieren können: Seiten öffnen, Formulare ausfüllen, Formulare senden. Und wenn Sie sich nicht für browser- und browserübergreifende Tests interessieren und plötzlich die Benutzeroberfläche testen, können Sie PhpBrowser verwenden. In der Regel verwenden wir es in unseren UI-Tests, da wir keine komplizierte Interaktionslogik benötigen, sondern nur die Seite öffnen und dort etwas Kleines tun müssen.
Das zweite Modul, das wir verwenden, ist REST. Ich denke, der Name macht deutlich, was er tut. Für alle http-Interaktionen können Sie dieses Modul verwenden. Es scheint mir, dass fast alle Interaktionen darin gelöst sind: Header, Cookies, Autorisierung. Alles was du brauchst ist drin.
Das dritte Modul, das wir sofort verwenden, ist das Db-Modul. In neueren Versionen von Codeception wurde dort nicht nur eine, sondern mehrere Datenbanken unterstützt. Wenn Sie plötzlich mehrere Datenbanken in Ihrem Projekt haben, funktioniert dies sofort.
Das Cli-Modul, mit dem Sie
Shell- und
Bash- Befehle aus Tests ausführen können, und wir verwenden es auch.
Es gibt ein AMQP-Modul, das mit allen Nachrichtenbrokern zusammenarbeitet, die auf diesem Protokoll basieren. Ich möchte darauf hinweisen, dass es offiziell auf RabbitMQ getestet wurde. Da wir RabbitMQ verwenden, ist alles in Ordnung mit ihm.
Tatsächlich deckt Codeception, zumindest in unserem Fall, 80-85% aller Aufgaben ab, die wir benötigen. Aber ich musste noch an etwas arbeiten.
Beginnen wir mit SOAP.

In unseren Diensten gibt es an einigen Stellen SOAP-Endpunkte. Sie müssen getestet, gezogen, etwas mit ihnen zu tun haben. Sie werden jedoch sagen, dass es in Codeception ein solches Modul gibt, mit dem Sie Anfragen senden und dann etwas mit den Antworten tun können. Irgendwie zu analysieren, Schecks hinzuzufügen und alles ist in Ordnung. Das SOAP-Modul funktioniert jedoch nicht sofort mit mehreren SOAP-Endpunkten.

Zum Beispiel haben wir Monolithen mit mehreren WSDLs und mehreren SOAP-Endpunkten. Dies bedeutet, dass es im Codeception-Modul unmöglich ist, dies in einer yml-Datei so zu konfigurieren, dass es mit mehreren arbeiten kann.

Codeception verfügt über eine dynamische Modulrekonfiguration, und Sie können eine Art Adapter schreiben, um beispielsweise ein SOAP-Modul zu empfangen und dynamisch neu zu konfigurieren. In diesem Fall müssen der Endpunkt und das verwendete Schema ersetzt werden. Wenn Sie dann im Test den Endpunkt ändern müssen, an den Sie eine Anfrage senden möchten, erhalten wir unseren Adapter und ändern ihn in einen neuen Endpunkt, in eine neue Schaltung und senden eine Anfrage an ihn.

In Codeception gibt es keine Arbeit mit Kafka und es gibt keine mehr oder weniger offiziellen Add-Ons von Drittanbietern, die mit Kafka zusammenarbeiten. Es gibt keinen Grund zur Sorge, wir haben unser Modul geschrieben.

Es ist also in einer yml-Datei konfiguriert. Einige Einstellungen sind für Broker, Verbraucher und Themen festgelegt. Diese Einstellungen können Sie beim Schreiben Ihres Moduls in Module mit der Initialisierungsfunktion ziehen und dieses Modul mit denselben Einstellungen initialisieren. Tatsächlich verfügt das Modul über alle anderen zu implementierenden Methoden: Fügen Sie die Nachricht in das Thema ein und lesen Sie sie. Das ist alles, was Sie von diesem Modul benötigen.
Fazit : Module für Codeception sind einfach zu schreiben.
Mach weiter. Wie gesagt, Codeception hat ein Cli-Modul - einen Wrapper für
Shell- Befehle und die Arbeit mit deren Ausgabe.

Manchmal muss der
Shell- Befehl jedoch nicht in Tests, sondern in der Anwendung ausgeführt werden. Im Allgemeinen sind Tests und Anwendungen leicht unterschiedliche Einheiten, sie können an verschiedenen Orten liegen. Tests können an einem Ort ausgeführt werden, und die Anwendung kann sich an einem anderen Ort befinden.
Warum müssen wir
Shell in Tests ausführen?
Wir haben Befehle in Anwendungen, die beispielsweise Warteschlangen in RabbitMQ analysieren und Objekte nach Status verschieben. Diese Befehle im Pro-Modus werden unter dem Supervisor gestartet. Der Supervisor überwacht deren Umsetzung. Wenn sie fallen, startet er sie erneut und so weiter.
Wenn wir testen, läuft der Supervisor nicht. Andernfalls werden die Tests instabil und unvorhersehbar. Wir selbst möchten den Start dieser Befehle innerhalb der Anwendung steuern. Daher müssen wir diese Befehle aus den Tests in der Anwendung ausführen. Wir verwenden zwei Optionen. Das eine, das andere - im Prinzip ist alles gleich und alles funktioniert.
Wie führe ich eine
Shell in einer Anwendung aus?
Erstens: Führen Sie die Tests an derselben Stelle aus, an der sich die Anwendung befindet. Da alle Anwendungen in Docker vorhanden sind, können Tests in demselben Container ausgeführt werden, in dem sich der Dienst selbst befindet.
Die zweite Option: Erstellen Sie einen separaten Container für Tests, einige
Testläufer , aber machen Sie ihn mit der Anwendung identisch. Das heißt, aus demselben Docker-Image, und dann funktioniert alles ähnlich.

Ein weiteres Problem, auf das wir bei den Tests gestoßen sind, ist die Arbeit mit verschiedenen Dateisystemen. Unten finden Sie ein Beispiel dafür, womit Sie arbeiten können und sollten. Die ersten drei sind für uns relevant. Dies sind Webdav, SFTP und das Amazon-Dateisystem.
Womit müssen Sie arbeiten?
- Webdav
- FTP / SFTP
- AWS S3
- Lokal
- Azure, Dropbox, Google Drive
Wenn Sie in Codeception stöbern, finden Sie einige Module für fast jedes mehr oder weniger beliebte Dateisystem.

Das einzige, was ich nicht gefunden habe, ist für Webdav. Aber diese Dateisysteme, plus oder minus, sind in Bezug auf die externe Arbeit mit ihnen gleich, und wir möchten mit ihnen auf die gleiche Weise arbeiten.
Wir haben unser Modul Flysystem geschrieben. Es liegt auf
Github im öffentlichen Bereich und unterstützt zwei Dateisysteme - SFTP und Webdav - und ermöglicht es Ihnen, mit beiden über dieselbe API zu arbeiten.

Holen Sie sich eine Liste der Dateien, bereinigen Sie das Verzeichnis, schreiben Sie eine Datei und so weiter. Wenn Sie dort auch das Amazon-Dateisystem hinzufügen, werden unsere Anforderungen definitiv abgedeckt.
Der nächste Punkt, denke ich, ist sehr wichtig für Autotests, insbesondere auf Systemebene, ist die Arbeit mit Datenbanken. Im Allgemeinen möchte ich, dass es wie auf dem Bild - VZHUH ist und alles startet, es funktioniert, und diese Datenbanken sollten in Tests weniger unterstützt werden.

Was sind die Hauptaufgaben, die ich hier sehe:
- So rollen Sie die Datenbank der gewünschten Struktur aus - Db
- So füllen Sie die Datenbank mit Testdaten - Db, Fixtures
- So treffen Sie Auswahlen und Überprüfungen - Db
Für alle 3 Aufgaben in Codeception gibt es 2 Module - Db, über die ich bereits gesprochen habe, ein anderes heißt Fixtures.
Von diesen 2 Modulen und 3 Aufgaben verwenden wir nur DB für die dritte Aufgabe.
Für die erste Aufgabe können Sie DB verwenden. Dort können Sie den SQL-Speicherauszug konfigurieren, von dem aus die Datenbank bereitgestellt wird. Nun, das Modul mit Fixtures. Ich denke, es ist klar, warum es benötigt wird.
Es wird Fixtures in Form von Arrays geben, die in der Datenbank beibehalten werden können.
Wie gesagt, die ersten beiden Aufgaben lösen wir etwas anders, jetzt werde ich Ihnen sagen, wie wir das machen.
Datenbankbereitstellung- Container mit PostgreSQL oder MySQL anheben
- Wir rollen alle Migrationen mit Doktrinmigrationen
Der erste betrifft die Bereitstellung einer Datenbank. Wie passiert das in Tests? Wir erhöhen den Container mit der gewünschten Datenbank - entweder PostgreSQL oder MySQL - und rollen dann alle erforderlichen Migrationen mithilfe von
Doktrinmigrationen . Alles, die Datenbank der gewünschten Struktur ist fertig, kann in Tests verwendet werden.
Warum wir keine Feuchtigkeit verwenden - denn dann muss es nicht unterstützt werden. Dies ist eine Art Speicherauszug, der bei den Tests liegt und ständig aktualisiert werden muss, wenn sich etwas in der Datenbank ändert. Es gibt Migrationen - es ist nicht erforderlich, einen Speicherauszug zu verwalten.
Der zweite Punkt ist die Erstellung von Testdaten. Wir verwenden nicht das Fixtures-Modul von Codeception, sondern das
Symfony- Bundle für Fixtures.

Es gibt einen
Link dazu und ein Beispiel, wie Sie Fixtures in der Datenbank erstellen können.
Ihr Gerät wird dann als ein Objekt der Domäne erstellt, es kann in der Datenbank gespeichert werden und die Testdaten sind bereit.
Warum DoctrineFixtureBundle?
- Einfachere Erstellung von Ketten verwandter Objekte.
- Weniger Datenverdoppelung, wenn Vorrichtungen für verschiedene Tests ähnlich sind.
- Weniger Änderungen beim Ändern der Datenbankstruktur.
- Fixture-Klassen sind viel visueller als Arrays.
Warum benutzen wir es? Ja, aus dem gleichen Grund - diese Geräte sind viel einfacher zu warten als Geräte von Codeception. Es ist einfacher, Ketten verwandter Objekte zu erstellen, da sich alles im Symfony-Bundle befindet. Es müssen weniger Daten dupliziert werden, da Fixtures vererbt werden können. Dies sind Klassen. Wenn sich die Datenbankstruktur ändert, müssen diese Arrays immer bearbeitet werden und Klassen nicht immer. Fixtures in Form von Domain-Objekten sind immer sichtbarer als Arrays.
Wir haben über Datenbanken gesprochen, lassen Sie uns ein wenig über Moki sprechen.
Da es sich um Tests auf einem ausreichend hohen Niveau handelt, die das gesamte System testen, und da unsere Systeme stark miteinander verbunden sind, ist klar, dass es einige Austausche und Interaktionen gibt. Jetzt werden wir über Mokeys über die Interaktion zwischen Systemen sprechen.
Regeln für mok- Weinen Sie alle externen http-Service-Interaktionen
- Überprüfen Sie nicht nur positive, sondern auch negative Szenarien
Interaktionen sind einige REST- oder SOAP-http-Interaktionen. All diese Wechselwirkungen im Rahmen der Tests, die wir benetzen. Das heißt, in unseren Tests gibt es nirgendwo einen wirklichen Anreiz für externe Systeme. Dies macht die Tests stabil. Da ein externer Dienst möglicherweise funktioniert, möglicherweise nicht funktioniert, möglicherweise langsam reagiert, möglicherweise im Allgemeinen schnell weiß, wie er sich verhält. Deshalb decken wir alles mit Moks ab.
Wir haben auch eine solche Regel. Wir befeuchten nicht nur positive Interaktionen, sondern versuchen auch, einige negative Fälle zu überprüfen. Wenn beispielsweise ein Dienst eines Drittanbieters mit einem 500. Fehler antwortet oder einen aussagekräftigeren Fehler erzeugt, versuchen wir, alles zu überprüfen.
Wir verwenden Wiremock für Mocks, Codeception selbst unterstützt ..., es hat ein solches offizielles Add-On Httpmock, aber wir mochten Wiremock mehr. Wie funktioniert es
Wiremock wird während der Tests als separater Docker-Container erstellt, und alle Anforderungen, die an das externe System gesendet werden müssen, gehen an Wiremock.

Wiremock, wenn Sie sich die Folie ansehen - es gibt ein solches Feld, Anforderungszuordnung. Es enthält eine Reihe solcher Zuordnungen, die besagen, dass Sie eine solche Antwort geben müssen, wenn eine solche Anforderung eintrifft. Alles ist sehr einfach: Eine Anfrage kam - erhielt einen Schein.
Mocks können statisch erstellt werden, dann wird der Container, wenn bereits mit Wiremock steigt, diese Mocks verfügbar sein, sie können für manuelle Tests verwendet werden. Sie können direkt im Code in einer Art Test dynamisch erstellen.
Hier ist ein Beispiel dafür, wie man ein Modell dynamisch erstellt. Die Beschreibung ist ziemlich deklarativ. Aus dem Code geht sofort hervor, welche Art von Modell wir erstellen: ein Modell für die GET-Methode, die zu einer solchen URL kommt, und tatsächlich, was zurückgegeben werden soll.

Zusätzlich zu der Tatsache, dass dieses Modell erstellt werden kann, hat Wiremock die Möglichkeit, zu überprüfen, welche Anforderung an dieses Modell gesendet wurde. Dies ist auch bei Tests sehr nützlich.
Über Codeception selbst, wahrscheinlich alles und ein paar Worte darüber, wie unsere Tests ausgeführt werden, und ein bisschen Infrastruktur.
Was benutzen wir?

Nun, erstens, alle Dienste, die wir in Docker haben. Wenn Sie also eine Testumgebung starten, werden die richtigen Container angehoben.
Make wird für interne Befehle verwendet, Bamboo wird als CI verwendet.
Wie sieht der CI-Testlauf aus?

Zuerst erstellen wir die gewünschte Version der Anwendung, dann erhöhen wir die Umgebung - dies ist die Anwendung, alle Dienste, die sie benötigt, wie Kafka, Rabbit, die Datenbank, und wir führen die Migration in die Datenbank durch.
All diese Umgebungen werden mithilfe von Docker Compose erstellt. In CI drehen sich alle Container unter Kubernetes. Führen Sie dann die Tests aus und führen Sie sie aus.
Wie lange dauert das alles?
Es hängt alles vom jeweiligen Dienst ab, aber in der Regel beträgt das Erhöhen der Umgebung vor dem Ausführen der Tests 5 bis 10 Minuten, Tests - von 6 bis 30 Minuten.

Ich werde diese Frage sofort warnen, während alle Tests in einem Thread ausgeführt werden.
Nun, so eine Frage. Wie oft sollten Tests durchgeführt werden? Natürlich, je öfter, desto besser. Je früher Sie ein Problem erkennen können, desto schneller können Sie es lösen.
Wir haben 2 Hauptregeln. Wenn eine Aufgabe getestet wird, müssen alle Tests sie bestehen, sowohl Unit- als auch nicht Unit-Tests. Wenn einige Tests fehlschlagen, ist dies eine Gelegenheit, die Aufgabe auf die Korrektur zu übertragen.Natürlich, wenn wir die Veröffentlichung herausbringen. Bei der Veröffentlichung müssen alle Tests bestanden werden.Am Ende möchte ich etwas Inspirierendes sagen - Tests schreiben, grün sein lassen, Codeception verwenden, Moki machen. Ich denke, Sie alle verstehen das perfekt.