End-to-End-Tests von Microservices mit Catcher

Published on January 08, 2019

End-to-End-Tests von Microservices mit Catcher

    Guten Tag! Ich möchte ein neues Tool für die End-to-End-Prüfung von Microservices vorstellen - Catcher
    Logo


    Warum testen?


    Warum brauchen Sie e2e-Tests? Martin Fowler empfiehlt , dies für einfachere Tests zu vermeiden.


    Je höher jedoch die Tests sind, desto weniger werden sie umgeschrieben. Unit-Tests werden fast vollständig umgeschrieben. Funktionstests müssen auch im Falle eines ernsthaften Refactorings Ihre Zeit verschwenden. End-to-End-Tests sollten die Geschäftslogik überprüfen, und dies ändert sich am wenigsten.


    Darüber hinaus garantiert eine vollständige Abdeckung aller Mikrodienste mit Tests nicht deren korrekte Interaktion. Entwickler können das Protokoll falsch implementieren (Fehler im Namen / Datentyp).


    Oder implementieren Sie eine neue Funktionalität, die sich auf das Datenschema aus der Dokumentation und auf die Produktumgebung stützt. Sie erhalten eine Überraschung in Form von Schemafehlern: ein Durcheinander in den Daten oder jemand, der vergessen hat, das Datenschema zu aktualisieren.


    Und die Tests aller beteiligten Dienste sind grün.


    Warum automatisches Testen?


    Wirklich. An meinem früheren Arbeitsplatz wurde entschieden, dass es zu lang, schwierig und teuer war, Zeit für die Implementierung automatischer Tests aufzuwenden. Das System ist nicht groß (10-15 Mikrodienste mit einem gemeinsamen Anteil). CTO entschied, dass "Tests nicht wichtig sind, die Hauptsache ist, dass das System funktioniert." Manuell in mehreren Umgebungen getestet.


    Wie es aussah (allgemeiner Prozess):


    1. Vereinbarung mit anderen Entwicklern (Rollout aller an der neuen Funktionalität beteiligten Microservices)
    2. Alle Services ausrollen
    3. Verbindung zu Remote-Kafka (Double SSH in DMZ)
    4. Stellen Sie eine Verbindung zu k8s-Protokollen her
    5. Manuelles Generieren und Senden einer Nachricht (Gott sei Dank Json)
    6. Beobachten Sie die Protokolle und versuchen Sie herauszufinden, ob es funktioniert hat oder nicht.

    Und jetzt ein wenig Teer in diesem Fass mit Schokolade: Die meisten Tests mussten durchgeführt werden, um Benutzer zu erstellen, da es schwierig ist, die vorhandenen wiederzuverwenden.


    Erstens aufgrund der Tatsache, dass das System verteilt ist - mehrere Dienste hatten ihre eigenen Datenbanken, die Informationen über Benutzer enthielten.


    Zweitens wurde Kafka zur dauerhaften Datenspeicherung verwendet. Dh Selbst wenn die Informationen in der Datenbank gelöscht / geändert werden, liest der Dienst sie beim Neustart immer noch zurück.


    Wie sah die Registrierung des neuen Testbenutzers (ungefähr) aus:


    1. Geben Sie beliebige Daten ein (Name, E-Mail usw.)
    2. Geben Sie persönliche Daten ein (Adresse, Telefonnummer, Steuerinformationen)
    3. Bankdaten eingeben (eigentlich Bankdaten)
    4. Beantworte 20-40 Fragen (fühlst du schon Schmerzen?)
    5. Pass IDNow Identifizierung (in der Entwicklungsumgebung , Gott sei Dank, wurde es ausgeschaltet, auf der Bühne sind es ungefähr 5 Minuten oder mehr, weil ihre Sandbox manchmal überlastet ist)
    6. In diesem Schritt ist die Eröffnung eines Kontos in einem Drittanbietersystem erforderlich, und Sie können über das Front-End nichts tun. Sie müssen über ssh zum kafka gehen und als Mock-Server arbeiten (senden Sie eine Nachricht, dass das Konto geöffnet ist)
    7. Als nächstes müssen Sie zu einem anderen Front-End im persönlichen Konto des Moderators gehen und den Benutzer bestätigen.

    Super, User ist registriert! Jetzt etwas mehr Teer: Für einige Tests benötigen Sie mehr als 1 Testbenutzer. Und manchmal werden die Tests vom ersten Mal an nicht bestanden.


    Und wie erfolgt die Überprüfung der neuen Funktionalität und Bestätigung durch das Business-Team?
    Trotzdem muss in der nächsten Umgebung wiederholt werden.


    Muss ich sagen, dass Sie sich nach einer Weile wie ein Affe fühlen, der nur noch die Tasten drückt und Benutzer registriert?


    Einige andere Entwickler (normalerweise das Front-End) hatten Probleme mit der Verbindung zum Kafka. Und mit einem Fehler im Terminal mit mehr als 80 Zeichen (nicht jeder wusste etwas über tmux).


    Vorteile :


    • Keine Notwendigkeit zu konfigurieren / schreiben. Testen Sie direkt in der laufenden Umgebung.
    • erfordert keine hohe Qualifikation (billigere Spezialisten können)

    Nachteile :


    • braucht viel Zeit (je weiter, desto mehr)
    • In der Regel wird nur die neue Funktionalität getestet (es ist nicht klar, ob die vorhandene defekt ist).
    • Oft sind erfahrene Entwickler an manuellen Tests beteiligt (teure Spezialisten leisten billige Arbeit).

    Wie automatisiere ich?


    Wenn Sie bis hierher lesen und mit dem Kopf nicken und sagen: „Ja, ein ausgezeichneter Prozess, Leute wissen, was sie tun“, dann werden Sie nicht weiter interessiert sein.


    Es gibt zwei Arten von selbst erstellten e2e-Tests, die davon abhängen, welcher Programmierer freier war:


    • das Backend, das in Ihrer Testumgebung lebt. Es enthält die Testlogik, die durch die Endpunkte wechselt. Durch die Interaktion mit CI kann es sogar teilweise automatisiert werden.
    • Das Skript mit derselben verkabelten Logik. Der einzige Unterschied ist, dass Sie irgendwohin müssen und von dort aus starten. Wenn Sie Ihrem CI vertrauen, können Sie es sogar automatisch ausführen.

    Hört sich gut an. Probleme?


    Ja, solche Tests beziehen sich auf das, was derjenige, der sie schreibt, weiß. In der Regel handelt es sich dabei um Skriptsprachen wie Ruby oder Python, mit denen Sie solche Dinge schnell und einfach schreiben können. Manchmal stößt man jedoch auf eine Reihe von Bash-Skripten, C oder etwas Exotischeres (ich habe eine Woche damit verbracht, ein Fahrrad für Bash-Skripten in eine Python umzuschreiben, weil die Skripten nicht mehr erweiterbar waren und niemand wirklich wusste, wie sie funktionierten oder was sie testeten). .
    Beispielprojekt hier


    Vorteile :


    • Automatisierung

    Nachteile :


    • Zusätzliche Anforderungen an die Qualifikation von Entwicklern sind möglich (wenn diese in Java entwickelt wurden und die Tests in Python geschrieben wurden)
    • Code schreiben, um geschriebenen Code zu testen (wer testet die Tests?)

    Gibt es etwas fertig?


    Es genügt natürlich, auf BDD zu schauen . Da ist Gurke , da ist Spur .


    Kurz gesagt, der Entwickler beschreibt ein Geschäftsskript in einer speziellen Sprache und implementiert dann die Skriptschritte im Code. Die Sprache ist im Allgemeinen für Menschen lesbar und es wird davon ausgegangen, dass sie nicht nur von Entwicklern, sondern auch von Projektmanagern gelesen / geschrieben wird.


    Die Szenarien zusammen mit der Implementierung der Schritte befinden sich ebenfalls in einem separaten Projekt und werden von Produkten von Drittanbietern (Cucumber / Gauge / ...) ausgeführt.


    Das Skript sieht folgendermaßen aus:


    Customer sign-up
    ================
    * Go to sign up page
    Customer sign-up
    ----------------
    tags: sign-up, customer
    * Sign up a new customer with name "John" email "jdoe@test.de" and "password"
    * Check if the sign up was successful

    Und Umsetzung:


    @Step("Sign up as <customer> with email <test@example.com> and <password>")
        public void signUp(String customer, String email, String password) {
            WebDriver webDriver = Driver.webDriver;
            WebElement form = webDriver.findElement(By.id("new_user"));
            form.findElement(By.name("user[username]")).sendKeys(customer);
            form.findElement(By.name("user[email]")).sendKeys(email);
            form.findElement(By.name("user[password]")).sendKeys(password);
            form.findElement(By.name("user[password_confirmation]")).sendKeys(password);
            form.findElement(By.name("commit")).click();
        }
        @Step("Check if the sign up was successful")
        public void checkSignUpSuccessful() {
            WebDriver webDriver = Driver.webDriver;
            WebElement message = webDriver.findElements(By.className("message"));
            assertThat(message.getText(), is("You have been signed up successfully!"));
        }

    Komplettes Projekt hier


    Vorteile :


    • Geschäftslogik wird in einer für Menschen lesbaren Sprache beschrieben und an einem Ort gespeichert (kann als Dokumentation verwendet werden)
    • Es werden vorgefertigte Lösungen verwendet, Entwickler müssen nur wissen, wie sie verwendet werden

    Nachteile :


    • Manager lesen und schreiben keine Skripte
    • Sie müssen sowohl die Spezifikationen als auch deren Implementierung im Auge behalten (und dies ist das Schreiben von Code und das Bearbeiten von Spezifikationen).

    Warum also der Fänger?


    Natürlich, um den Prozess zu vereinfachen.


    Der Entwickler schreibt nur Skripte in json / yaml und Catcher führt sie aus. Das Skript besteht aus nacheinander ausgeführten Schritten, zum Beispiel:


    steps:
        - http:
            post:
              url: '127.0.0.1/save_data'
              body: {key: '1', data: 'foo'}
        - postgres:
            request:
              conf: 'dbname=test user=test host=localhost password=test'
              query: 'select * from test where id=1'

    Catcher unterstützt jinja2-Muster, sodass Sie im obigen Beispiel Variablen anstelle von verdrahteten Werten verwenden können. Globale Variablen können in Inventardateien (wie in einem Ansible) gespeichert, aus der Umgebung abgerufen und neue registriert werden:


    variables:
      bonus: 5000
      initial_value: 1000
    steps:
    - http:
            post:
              url: '{{ user_service }}/sign_up'
              body: {username: 'test_user_{{ RANDOM_INT }}', data: 'stub'}
            register: {user_id: '{{ OUTPUT.uuid }}'
    - kafka:
            consume:
                server: '{{ kafka }}'
                topic: '{{ new_users_topic }}'
                where:
                    equals: {the: '{{ MESSAGE.uuid }}', is: '{{ user_id }}'}
            register: {balance: '{{ OUTPUT.initial_balance }}'}

    Darüber hinaus können Sie Überprüfungsschritte ausführen:


    - check: # check user’s initial balance
        equals: {the: '{{ balance }}', is: '{{ initial_value + bonus }}'}

    Sie können auch einige Skripte aus anderen Skripten ausführen, was sich erheblich auf die Sauberkeit und die Wiederverwendung von Code auswirkt (einschließlich der Ausführung nur eines Teils der Schritte durch das Tag-System, des verzögerten Starts usw., Buns).


    include:
        file: register_user.yaml
        as: sign_up
    steps:
        # .... some steps
        - run:
            include: sign_up
        # .... some steps

    Das Einfügen und Verwenden von Skripten kann das Problem des Wartens auf eine Ressource lösen (Warten auf den Dienst, während dieser gestartet wird).


    Zusätzlich zu vorgefertigten integrierten Schritten und einem zusätzlichen Repository können Sie Ihre eigenen Module in Python (einfach durch Erben von ExternalStep ) oder in einer anderen Sprache schreiben :


    #!/bin/bash
    one=$(echo ${1} | jq -r '.add.the')
    two=$(echo ${1} | jq -r '.add.to')
    echo $((${one} + ${two}))

    und benutze:


    ---
    variables:
      one: 1
      two: 2
    steps:
        - math:
            add: {the: '{{ one }}', to: '{{ two }}'}
            register: {sum: '{{ OUTPUT }}'}

    Skripte werden in der Docker-Datei abgelegt und über CI ausgeführt.


    Dieses Image kann auch in Marathon / K8 verwendet werden, um eine vorhandene Umgebung zu testen. Momentan arbeite ich an einem Backend (analog zu AnsibleTower), um den Testprozess noch einfacher und komfortabler zu gestalten.


    Vorteile :


    • Code muss nicht geschrieben werden (nur bei benutzerdefinierten Modulen)
    • Wechseln der Umgebung durch Inventardateien (wie in Ansible)
    • Sie können Ihre eigenen Module verwenden (in jeder Sprache, auch sh)

    Nachteile :


    • nicht von Menschen lesbare Syntax (im Vergleich zu BDD-Tools)

    Anstelle des Abschlusses


    Als ich dieses Tool schrieb, wollte ich nur die Zeit reduzieren, die ich normalerweise für Tests verbringe. So kam es, dass man in jeder neuen Firma ein solches System schreiben (oder neu schreiben) musste.


    Das Tool erwies sich jedoch als flexibler als ich erwartet hatte. Wenn sich jemand für einen Artikel (oder das Tool selbst) interessiert, kann ich Ihnen sagen, wie Sie Catcher für die Organisation zentraler Migrationen und die Aktualisierung von Microservice-Systemen verwenden.


    Upd


    Wie ich in den Kommentaren ausgeführt habe, wird das Thema nicht veröffentlicht.
    Ich werde versuchen, hier die umstrittensten Thesen anzugeben.


    • End-to-End-Tests sind keine Komponententests. Ich habe in diesem Artikel bereits auf M. Fowler hingewiesen . Unit - Tests befinden sich im Test - Backend - Projekt (Standardverzeichnistests ) und werden jedes Mal ausgeführt, wenn sich der Code in CI ändert. Und e2e-Tests sind ein separates Projekt, sie laufen in der Regel länger, testen das Zusammenspiel aller beteiligten Dienste und wissen nichts über den Code Ihres Projekts (Black Box).
    • Verwenden Sie den Catcher nicht für Integrationstests (und die folgenden). Ist es teuer. Es ist viel schneller, einen Test für das aktuelle Backend auf Ihrem PL zu schreiben. Sie benötigen End-to-End-Tests nur, wenn Ihre Geschäftslogik auf zwei oder mehr Services verteilt ist.
    • Catcher ist auch BDD. Aus meiner Sicht liegt der Hauptvorteil gegenüber Gauge / Cucumber in vorgefertigten Modulen und der einfachen Hinzufügung. Im Idealfall wird nur der Test geschrieben. In der letzten Firma habe ich alle 4 Tests an Standardkomponenten geschrieben, ohne etwas zu programmieren. Dementsprechend sind die Qualifikationsanforderungen (und der Preis eines solchen Spezialisten) niedriger. Sie müssen nur json / yaml und die Fähigkeit zum Lesen von Spezifikationen kennen.
    • Um Catcher-Tests schreiben zu können, müssen Sie Catcher-DSL studieren. Leider ist es wahr. Zunächst wollte ich die Tests selbst direkt vom Mikrofon aus schreiben. Aber dann dachte ich, dass ich dann als überflüssig abgetan würde;) Wie oben erwähnt, ist der Catcher DSL der Standard json / yaml und die Spezifikation der Schritte. Nichts grundsätzlich Neues.
    • Sie können Standardtechnologie verwenden und etwas Eigenes schreiben. Wir sprechen jedoch über Microservices. Dies ist eine große Anzahl von verschiedenen Technologien und PL und eine große Anzahl von Teams. Und wenn für den Java-Befehl junit + testcontainers die naheliegende Wahl ist, wird der Befehl erlang etwas anderes wählen. In einem großen Unternehmen mit mehr als 30 Teams an der Spitze wird entschieden, dass alle Tests an das neue Infrastruktur- / QA-Team übergeben werden sollen. Präsentieren Sie, wie sie sich über diesen Zoo freuen werden?
    • Wenn Sie 4-5 e2e-Tests haben, können Sie alles in einer beliebigen Skriptsprache schreiben und vergessen. Wenn sich die Logik jedoch im Laufe der Zeit ändert, müssen Sie in 2 bis 4 Jahren eine Umgestaltung vornehmen, indem Sie die Geschäftslogik der Tests und die Implementierung von Zugriffsmethoden direkt an die zu testenden Komponenten übergeben. Am Ende schreiben Sie also Ihren Catcher, nur nicht so flexibel. Ich habe 4 Implementierungen gebraucht, um das zu verstehen;)