Hallo Habr!
Mein Name ist Vitaliy Kotov, ich mache viel Testautomatisierung und es gefällt mir. Ich habe kürzlich an einem Projekt zur Konfiguration der Automatisierung von Grund auf auf dem TypeScript + Protractor + Jasmine-Stack teilgenommen. Für mich war dieser Stapel neu und ich suchte im Internet nach den notwendigen Informationen.
Es gelang mir, die nützlichsten und sinnvollsten Handbücher nur auf Englisch zu finden. Ich entschied, dass ich dies auch auf Russisch tun musste. Ich möchte Ihnen nur die Grundlagen erläutern: Warum ein solcher Stack, was Sie konfigurieren müssen und wie der einfachste Test aussieht.
Ich muss sofort sagen, dass ich selten mit NodeJS, npm und serverseitigem JavaScript im Allgemeinen arbeite (insbesondere mit TypeScript). Wenn Sie irgendwo einen Fehler in der Terminologie finden oder einige meiner Entscheidungen verbessert werden können, werde ich mich freuen, dies in den Kommentaren erfahrener Leute zu erfahren.
Übrigens hatte ich bereits einen ähnlichen Artikel:
„Wir stellen die Automatisierung in ein paar Stunden bereit: PHPUnit, Selenium, Composer“ .

Herausforderung
Lassen Sie uns zunächst herausfinden, welches Problem wir lösen. Wir haben eine Webanwendung, die mit AngularJS geschrieben wurde. Dies ist ein JavaScript-Framework, auf dessen Grundlage häufig Webprojekte geschrieben werden.
In diesem Artikel werden die Vor- und Nachteile von AngularJS-Projekten nicht berücksichtigt. Nur ein paar Worte zu den Merkmalen solcher Projekte beim Schreiben von e2e-Tests für sie.
Ein ziemlich wichtiger Aspekt beim Testen der Automatisierung ist das Arbeiten mit Seitenelementen, was mithilfe von Locators geschieht. Ein Locator ist eine Zeile, die nach bestimmten Regeln zusammengesetzt ist und ein UI-Element identifiziert: eines oder mehrere.
Für das Web werden am häufigsten CSS und Xpath verwendet. Wenn sich auf der Seite ein Element mit einer eindeutigen ID befindet, können Sie manchmal danach suchen. Es scheint mir jedoch, dass WebDriver diese ID am Ende immer noch in einen CSS-Locator verwandelt und bereits damit arbeitet.
Wenn wir uns den HTML-Code eines AngularJS-Projekts ansehen, sehen wir viele Attribute für Elemente, die nicht in klassischem HTML enthalten sind:

Der Code stammt von der
Winkelmesser-Demo- Seite.
Alle Attribute, die mit ng- * beginnen, werden von AngularJS verwendet, um mit der Benutzeroberfläche zu arbeiten. Eine ziemlich typische Situation ist, wenn die Elemente außer diesen Steuerattributen keine anderen Attribute haben, was den Prozess des Kompilierens von Qualitätslokalisatoren etwas erschwert.
Diejenigen, die viel automatisieren, wissen um den Wert solcher Benutzeroberflächen, für die Locators einfach erstellt werden können. Immerhin ist dies bei großen Projekten selten. :) :)
Tatsächlich müssen wir für ein solches Projekt auch die Testautomatisierung konfigurieren. Lass uns gehen!
Was ist was?
Lassen Sie uns zunächst herausfinden, warum jede Komponente unseres Stapels benötigt wird.
Winkelmesser ist ein Testframework, das auf WebDriverJS basiert. Er wird unsere Browser starten, sie zwingen, die erforderlichen Seiten zu öffnen und mit den erforderlichen Elementen zu interagieren.
Dieses Framework ist speziell auf AngularJS-Projekte zugeschnitten. Es bietet zusätzliche Möglichkeiten, Locators anzugeben:
element(by.model('first')); element(by.binding('latest')); element(by.repeater('some'));
Eine vollständige Liste finden Sie auf der
Handbuchseite .
Diese Methoden vereinfachen die Erstellung und Unterstützung von Locators in einem Projekt. Sie müssen jedoch verstehen, dass "unter der Haube" all dies auf jeden Fall in CSS konvertiert wird. Tatsache ist, dass das W3C-Protokoll, auf dessen Grundlage die Interaktion in WebDriver stattfindet, nur mit einer endlichen Menge von Locators arbeiten kann. Diese Liste kann auf
w3.org eingesehen
werden .
TypeScript ist eine von Microsoft erstellte Programmiersprache. TypeScript unterscheidet sich von JavaScript durch die Fähigkeit, Variablen einzugeben, die Verwendung vollwertiger Klassen zu unterstützen und Module zu verbinden.
In TS-Code geschrieben, um mit der V8-Engine zu arbeiten, wird in JS-Code übersetzt, der bereits ausgeführt wird. Während dieser Transformation wird der Code auf Konformität überprüft. Beispielsweise wird es nicht „kompiliert“, wenn anstelle von int irgendwo explizit eine Zeichenfolge an die Funktion übergeben wird.
Jasmine ist ein Framework zum Testen von JavaScript-Code. In der Tat ist es ihm zu verdanken, dass unser JS-Code zu dem wird, was wir früher als Test bezeichnet haben. Er verwaltet diese Tests.
Im Folgenden sehen wir uns die Möglichkeiten an.
Montage und Projekteinrichtung
Nun, wir haben uns für eine Reihe von Frameworks entschieden. Lassen Sie uns nun das Ganze zusammenstellen.
Um mit dem Code zu arbeiten, habe ich
Visual Studio Code von Microsoft ausgewählt. Obwohl viele in
WebStorm oder sogar
Intellij Idea von JetBrains schreiben.
Ich habe bereits NodeJS (v11.6.0) und NPM (6.9.0) installiert. Wenn Sie es nicht haben, ist dies kein Problem. Die Installation ist nicht schwierig. Alles ist auf der
offiziellen Website ausreichend detailliert beschrieben.
Garn kann anstelle von NPM verwendet werden, obwohl dies für ein kleines Projekt nicht wichtig ist.
In unserer IDE erstellen wir ein neues Projekt. Wir erstellen package.json im Stammverzeichnis des Projekts - darin beschreiben wir alle Pakete, die wir für das Projekt benötigen.
Sie können es mit dem Befehl
npm init erstellen. Oder Sie können den Inhalt einfach in eine Datei kopieren.
Anfangs sieht
package.json folgendermaßen aus:
{ "name": "protractor", "dependencies": { "@types/node": "^10.5.2", "@types/jasmine": "^3.3.12", "protractor": "^5.4.2", "typescript": "^3.4.1" } }
Danach führen wir den Befehl
npm install aus , um alle erforderlichen Module und ihre Abhängigkeiten zu installieren (Sie erinnern sich an das Bild von etwas, das schwerer als ein Schwarzes Loch ist ...).
Daher sollten wir ein Verzeichnis node_modules haben. Wenn sie aufgetaucht ist, läuft alles nach Plan. Wenn nicht, lohnt es sich, das Ergebnis der Befehlsausführung zu untersuchen. In der Regel wird dort alles ausführlich beschrieben.
TypeScript und seine Konfiguration
Um TypeScript zu installieren, benötigen wir npm:
npm install -g typescript
Stellen Sie sicher, dass es installiert ist:
$ tsc -v Version 3.4.1
Alles scheint in Ordnung zu sein.
Jetzt müssen wir eine Konfiguration für die Arbeit mit TS erstellen. Es sollte sich auch im Stammverzeichnis des Projekts befinden und
tsconfig.json heißen
Der Inhalt wird folgendermaßen aussehen:
{ "compilerOptions": { "lib": ["es6"], "strict": true, "outDir" : "output_js", "types" : ["jasmine", "node"] }, "exclude": [ "node_modules/*" ] }
Kurz gesagt, wir haben in dieser Konfiguration Folgendes angegeben:
- In welches Verzeichnis soll der endgültige JS-Code gestellt werden (in unserem Fall ist es output_js)?
- Aktivieren Sie den strengen Modus
- Gibt an, mit welchen Frameworks wir arbeiten
- Node_modules von der Kompilierung ausgeschlossen
TS hat eine Vielzahl von Einstellungen. Das reicht für unser Projekt. Weitere
Informationen finden Sie
unter typescriptlang.org .
Nun wollen wir sehen, wie der Befehl
tsc funktioniert , der unseren TS-Code in JS-Code verwandelt. Erstellen Sie dazu eine einfache check_tsc.ts-Datei mit folgendem Inhalt:
saySomething("Hello, world!"); function saySomething(message: string) { console.log(message); }
Führen Sie dann den Befehl
tsc aus (dazu müssen Sie sich im Projektverzeichnis befinden).
Wir werden sehen, dass wir das Verzeichnis output_js haben und eine ähnliche js-Datei mit dem folgenden Inhalt darin erschienen ist:
"use strict"; saySomething("Hello, world!"); function saySomething(message) { console.log(message); }
Diese Datei kann bereits mit dem Befehl node gestartet werden:
$ node output_js/check_tsc.js Hello, world!
Also haben wir unser erstes TypeScipt-Programm geschrieben, herzlichen Glückwunsch. Schreiben wir jetzt Tests. :) :)
Winkelmesserkonfiguration
Für Winkelmesser benötigen wir auch eine Konfiguration. Aber es wird nicht mehr in Form von json sein, sondern in Form einer ts-Datei. Nennen wir es config.ts und schreiben dort den folgenden Code:
import { Config } from "protractor"; export const config: Config = { seleniumAddress: "http://127.0.0.1:4444/wd/hub", SELENIUM_PROMISE_MANAGER: false, capabilities: { browserName: "chrome", }, specs: [ "Tests/*Test.js", ] };
In dieser Datei haben wir Folgendes angegeben:
Erstens der Pfad zum laufenden Selenium-Server. Die Ausführung ist recht einfach. Sie müssen lediglich
die JAR-Datei des Standalone-Servers und die erforderlichen Treiber
herunterladen (z. B. den Chrome-Treiber
für den Chrome-Browser ). Schreiben Sie als nächstes den folgenden Befehl:
java -jar -Dwebdriver.chrome.driver=/path/to/chromedriver /path/to/selenium-server-standalone.jar
Als Ergebnis sollten wir die folgende Schlussfolgerung sehen:
23:52:41.691 INFO [GridLauncherV3.launch] - Selenium build info: version: '3.11.0', revision: 'e59cfb3' 23:52:41.693 INFO [GridLauncherV3$1.launch] - Launching a standalone Selenium Server on port 4444 2019-05-02 23:52:41.860:INFO::main: Logging initialized @555ms to org.seleniumhq.jetty9.util.log.StdErrLog 23:52:42.149 INFO [SeleniumServer.boot] - Welcome to Selenium for Workgroups.... 23:52:42.149 INFO [SeleniumServer.boot] - Selenium Server is up and running on port 4444
Port 4444 ist voreingestellt. Sie kann mit dem Parameter -port oder über den Konfigurationsparameter "seleniumArgs" => "-port" eingestellt werden.
Wenn Sie einfacher und schneller wollen: Sie können das npm-Paket
webdriver-manager herunterladen.
Verwalten Sie dann den Server mit den Befehlen start, shutdown usw. Es gibt keinen großen Unterschied, ich bin nur eher daran gewöhnt, mit einer JAR-Datei zu arbeiten. :) :)
Zweitens haben wir angegeben, dass wir den Promise-Manager nicht verwenden möchten. Dazu später mehr.
Drittens haben wir Funktionen für unseren Browser festgelegt. Ich habe zum Beispiel einen Teil kommentiert, dass wir den Browser ganz einfach im Headless-Modus starten können. Dies ist eine coole Funktion, mit der Sie unsere Tests jedoch nicht visuell beobachten können. In der Zwischenzeit lernen wir nur - ich würde gerne. :) :)
Viertens haben wir eine Maske für Spezifikationen (Tests) angegeben. Alles, was im Ordner Tests liegt und mit Test.js endet. Warum auf js, nicht ts? Dies liegt daran, dass Node letztendlich speziell mit JS-Dateien und nicht mit TS-Dateien arbeitet. Es ist wichtig, nicht verwirrt zu sein, besonders zu Beginn der Arbeit.
Erstellen Sie nun den Ordner Tests und schreiben Sie den ersten Test. Er wird Folgendes tun:
- Deaktiviert die Überprüfung, ob es sich um eine Winkelseite handelt. Ohne dies erhalten wir die folgende Fehlermeldung: Fehler beim Ausführen von testForAngular. Für Angular-Seiten ist diese Prüfung natürlich nicht erforderlich, um sie auszuschalten.
- Geht zur Google-Seite.
- Überprüfen Sie, ob ein Texteingabefeld vorhanden ist.
- Geben Sie den Text "Winkelmesser" ein.
- Klicken Sie auf die Schaltfläche "Senden" (es hat einen ziemlich komplizierten Locator, da es zwei Schaltflächen gibt und die erste unsichtbar ist).
- Es wird erwartet, dass die URL das Wort "Winkelmesser" enthält - dies bedeutet, dass wir alles richtig gemacht haben und die Suche begann.
Hier ist der Code, den ich bekommen habe:
import { browser, by, element, protractor } from "protractor"; describe('Search', () => { it('Open google and find a text', async () => {
Im Code sehen wir, dass alles mit der Funktion description () beginnt. Sie kam aus dem Jasmin-Framework zu uns. Dies ist ein Wrapper für unser Skript. Darin können Funktionen vorAll () und vorEach () enthalten sein, um Manipulationen vor allen Tests und vor jedem Test durchzuführen. So viele Funktionen wie es () sind, sind in der Tat unsere Tests. Am Ende werden, falls definiert, afterAll () und afterEach () für Manipulationen nach jedem Test und allen Tests ausgeführt.
Ich werde nicht über alle Funktionen von Jasmine sprechen, Sie können darüber auf der Website
jasmine.imtqy.com lesenUm unseren Test auszuführen, müssen Sie zuerst den TS-Code in JS-Code umwandeln und dann ausführen:
$ tsc $ protractor output_js/config.js
Unser Test hat begonnen - wir sind großartig. :) :)

Wenn der Test nicht gestartet wurde, lohnt es sich zu überprüfen:
- Dass der Code richtig geschrieben ist. Wenn der Code kritische Fehler enthält, werden diese im Allgemeinen während des Befehls tsc abgefangen.
- Dieser Selenium Server läuft. Dazu können Sie die URL http: //127.0.0.1-00-00444/wd/hub öffnen - es sollte eine Schnittstelle für Selenium-Sitzungen geben.
- Das Chrome startet normalerweise mit der heruntergeladenen Version des Chrome-Treibers. Klicken Sie dazu auf der Seite wd / hub / auf Sitzung erstellen und wählen Sie Chrome aus. Wenn es nicht gestartet wird, müssen Sie entweder Chrome aktualisieren oder eine andere Version des Chrome-Treibers herunterladen.
- Wenn dies alles fehlschlägt, können Sie überprüfen, ob der Befehl npm install erfolgreich ausgeführt wurde.
- Wenn alles richtig geschrieben ist, aber immer noch nichts startet, versuchen Sie, den Fehler zu googeln. Es hilft meistens. :) :)
NPM-Skripte
Um das Leben einfacher zu machen, können Sie einen Teil der Befehle npm Aliase machen. Zum Beispiel möchte ich das Verzeichnis mit früheren JS-Dateien löschen und vor jedem Testlauf mit neuen neu erstellen.
Fügen Sie dazu das Skriptelement zu package.json hinzu:
{ "name": "protractor", "scripts": { "test": "rm -rf output_js/; tsc; protractor output_js/config.js" }, "dependencies": { "@types/node": "^10.5.2", "@types/jasmine": "^3.3.12", "protractor": "^5.4.2", "typescript": "^3.4.1" } }
Wenn Sie nun den Befehl
npm test eingeben, geschieht Folgendes: Das Verzeichnis output_js mit dem alten Code wird gelöscht, neu erstellt und ein neuer JS-Code wird darauf geschrieben. Danach beginnen die Tests sofort.
Anstelle dieses Befehlssatzes können Sie auch andere Befehle angeben, die Sie persönlich zum Arbeiten benötigen. Beispielsweise können Sie einen Selenserver zwischen Testläufen starten und löschen. Dies ist natürlich einfacher im Testcode selbst zu steuern.
Ein bisschen über Promise
Am Ende werde ich ein wenig über Promise, async / await und darüber sprechen, wie sich das Schreiben von Tests in NodeJS von Java oder Python unterscheidet.
JavaScript ist eine asynchrone Sprache. Dies bedeutet, dass der Code nicht immer in der Reihenfolge ausgeführt wird, in der er geschrieben wurde. Dies schließt HTTP-Anforderungen ein, und wir erinnern uns, dass der Code über HTTP mit Selenium Server kommuniziert.
Versprechen (normalerweise als "Versprechen" bezeichnet) bieten eine bequeme Möglichkeit, asynchronen Code zu organisieren. Weitere
Informationen finden Sie unter
learn.javascript.ru .
Tatsächlich sind dies Objekte, die einen Code von der Ausführung eines anderen abhängig machen und dadurch eine bestimmte Reihenfolge garantieren. Winkelmesser arbeitet sehr aktiv mit diesen Objekten.
Schauen wir uns ein Beispiel an. Angenommen, wir führen den folgenden Code aus:
driver.findElement().getText();
In Java erwarten wir, dass wir ein Objekt vom Typ String zurückgeben. In Protractor ist dies nicht ganz so, wir werden ein Promise-Objekt zurückgeben. Und einfach so funktioniert das Drucken mit dem Ziel des Debuggens nicht.
Normalerweise müssen wir den resultierenden Wert nicht drucken. Wir müssen es an eine andere Methode übergeben, die bereits mit diesem Wert funktioniert. Beispielsweise wird der Text mit dem erwarteten Ergebnis verglichen.
Ähnliche Methoden in Protractor akzeptieren auch Promise-Objekte als Eingabe, sodass es kein Problem gibt. Wenn Sie den Wert dennoch anzeigen möchten, ist () hilfreich.
So können wir den Schaltflächentext auf einer Google-Seite drucken (da dies eine Schaltfläche ist, befindet sich der Text innerhalb des Wertattributs):
Bei den Schlüsselwörtern async / await handelt es sich um einen etwas neueren Ansatz für die Arbeit mit asynchronem Code. Es ermöglicht Ihnen, die Versprechen Hölle zu vermeiden, die zuvor im Code aufgrund der großen Anzahl von Verschachtelungen gebildet wurde. Trotzdem können Sie Promise nicht vollständig loswerden und müssen in der Lage sein, mit ihnen zu arbeiten. Dies ist verständlich und detailliert finden Sie im Artikel
Design async / await in JavaScript: Stärken, Fallstricke und Nutzungsmerkmale .
Hausaufgaben
Als Hausaufgabe schlage ich vor, Tests für eine in AngularJS geschriebene Seite zu schreiben:
Winkelmesser-Demo .
Vergessen Sie nicht, die Zeile aus dem Code zum Deaktivieren der Seitenprüfung in AngularJS zu entfernen. Arbeiten Sie unbedingt mit Locatoren, die speziell für AngularJS entwickelt wurden. Darin liegt keine besondere Magie, aber es ist sehr praktisch.
Zusammenfassung
Lassen Sie uns Bilanz ziehen. Wir haben es geschafft, Tests zu schreiben, die mit einer Reihe von TypeScript + Protractor + Jasmine funktionieren. Wir haben gelernt, wie man ein solches Projekt erstellt, die erforderlichen Konfigurationen erstellt und den ersten Test geschrieben.
Unterwegs haben wir ein wenig über die Arbeit mit JavaScript-Autotests gesprochen. Es scheint für ein paar Stunden gut zu sein. :) :)
Was zu lesen, wo zu suchen
Winkelmesser hat ein ziemlich gutes Handbuch mit JavaScript-Beispielen:
https://www.protractortest.org/#/tutorialJasmine hat ein Handbuch:
https://jasmine.imtqy.com/pages/docs_home.htmlTypeScipt hat einen guten Einstieg:
https://www.typescriptlang.org/docs/handbook/typescript-in-5-minutes.htmlAuf dem Medium gibt es einen guten Artikel in englischer Sprache über TypeScript + Winkelmesser + Gurke:
https://medium.com/@igniteram/e2e-testing-with-protractor-cucumber-using-typescript-564575814e4aUnd in meinem Repository habe ich den endgültigen Code für das veröffentlicht, was wir in diesem Artikel besprochen haben:
https://github.com/KotovVitaliy/HarbProtractorJasmineJasmine .
Im Internet finden Sie Beispiele für komplexere und größere Projekte auf diesem Stapel.
Vielen Dank für Ihre Aufmerksamkeit! :) :)