Automatisierung mit Codeception + Gherkin + PageObject für die Kleinsten


Nachdem ich kein einziges konkretes Beispiel für die Gherkin-Implementierung mit dem Seitenobjekt- Entwurfsmuster für Codeception im Internet gefunden hatte , dachte ich, dass es nicht unangebracht wäre, dem Internet von unserer eigenen Implementierung dieses Musters zu erzählen.

Dieser Artikel richtet sich eher an Personen, die bereits ein wenig mit Codeception oder ähnlichen Frameworks vertraut sind, aber noch nicht wissen, wie sie Test Object verwenden können, um Tests lesbarer zu machen, ihre Unterstützung zu vereinfachen und die Menge an zusätzlichem Code zu reduzieren. Trotzdem habe ich versucht, Schritt für Schritt alle wichtigen Punkte der Zusammenstellung des Automatisierungsprojekts von Grund auf zu erläutern.

Mit der Seitenobjektvorlage können Sie die Arbeit mit Seitenelementen kapseln, wodurch Sie die Codemenge reduzieren und die Unterstützung vereinfachen können. Alle Änderungen in der Benutzeroberfläche sind einfach und schnell zu implementieren. Aktualisieren Sie einfach die Seitenobjektklasse, die diese Seite beschreibt. Ein weiterer wichtiger Vorteil dieses Architekturansatzes besteht darin, dass Sie das HTML-Testskript nicht mit Details überladen können, wodurch es verständlicher und einfacher zu verstehen ist.

So sieht der Test ohne Verwendung von Page Object aus



Seitenobjekt verwenden



Ich werde mich nicht mit der Installation der Basisumgebung befassen, sondern meine ersten Daten angeben:

  • Ubuntu bionischer Biber
  • PHP 7.1.19-1
  • Composer - Abhängigkeitsverwaltungsmanager für PHP, global installiert
  • PhpStorm - Entwicklungsumgebung

Um die Tests durchzuführen, benötigen wir außerdem:


Erweitern Sie Codeception


Fahren Sie mit der Installation von Codeception fort:

Wir öffnen im Terminal das Verzeichnis, in dem wir das Projekt sammeln, oder erstellen ein Verzeichnis für das Projekt und gehen dorthin:

mkdir ProjectTutorial cd ProjectTutorial 

Installieren Sie das Codeception-Framework und seine Abhängigkeiten:

 composer require codeception/codeception --dev 



Die Installationsdatei für die Datei composer.json im Projekt sieht folgendermaßen aus:

 { "require": { "php": ">=5.6.0 <8.0", "facebook/webdriver": ">=1.1.3 <2.0", "behat/gherkin": "^4.4.0", "codeception/phpunit-wrapper": "^6.0.9|^7.0.6" }, "require-dev": { "codeception/codeception": "^2.5", "codeception/base": "^2.5" } } 

Erweitern Sie das Projekt:

 php vendor/bin/codecept bootstrap 



Weitere Informationen zur Installation des Projekts finden Sie in der offiziellen Dokumentation .

Zu diesem Zeitpunkt werden in unserem Projekt drei Testreihen erstellt. Standardmäßig unterteilt Codeception sie in Akzeptanz, Funktion und Einheit. Für diese Sets generiert Codeception außerdem drei yml-Dateien. In ihnen geben wir alle notwendigen Konfigurationen an , verbinden die Module und Eigenschaften, um unsere Tests durchzuführen.

Diese Lektion basiert auf dem Beispiel des Abnahmetests, daher werde ich die Einstellungen in Acceptance.suite.yml angeben.

Öffnen Sie unser Projekt in PHP Storm (oder einer anderen bevorzugten Entwicklungsumgebung) und gehen Sie zu Acceptance.suite.yml (standardmäßig befindet es sich im Ordner tests / accept.suite.yml).
Wir schreiben die minimal notwendigen Abhängigkeiten auf und achten immer auf die Formatierung. Die Module sind durch ein "-" gekennzeichnet und müssen sich auf derselben Ebene befinden. Andernfalls treten beim Teststart Fehler auf.

Es stellt sich heraus:

 actor: AcceptanceTester modules: enabled: - WebDriver: url: 'http://yandex.ru/' //    ,        browser: 'chrome' - \Helper\Acceptance //          gherkin: contexts: default: - AcceptanceTester 

Und noch etwas Vorarbeit:

Erstellen Sie ein separates Verzeichnis im Projektstamm (ich habe lib).
Erstellen Sie in diesem Verzeichnis die ausführbare Datei run.sh, in der Selenium und Chrome Driver ausgeführt werden.

Wir setzen hier Selenium und Chrome Driver und schreiben den Befehl run in run.sh:

 java -jar -Dwebdriver.chrome.driver=chromedriver_241 selenium-server-standalone-3.14.0.jar 

Wie es im Projekt aussieht:



Wir kehren zur Konsole zurück und ändern die Zugriffsrechte:

 chmod +x ./run.sh 

(Hinweis. Die Namen der im Verzeichnis liegenden Treiber müssen genau mit den im Startbefehl angegebenen übereinstimmen.)

Sie können Selenium und Webdriver jetzt starten, um nicht darauf zurückzukommen. Öffnen Sie dazu eine neue Registerkarte "Terminal", wechseln Sie in das Verzeichnis, in dem sich die Datei "run.sh" befindet, und schreiben Sie den Startbefehl:

 ~/AutomationProjects/ProjectTutorial/lib$ ./run.sh 

Stellen Sie sicher, dass der Server ausgeführt wird:



Wir lassen es in einem laufenden Zustand. Damit sind die Vorarbeiten abgeschlossen.

Schreiben eines Testskripts


Wir erstellen eine Feature-Datei für unseren Testfall. Zu diesem Zweck wird in Codeception ein spezieller Befehl bereitgestellt. Führen Sie ihn in der Konsole aus:

 cept g:feature acceptance check 

(Hinweis "check" - der Name meines Tests)

Wir sehen die neue Datei check.feature im Akzeptanzordner.



Wir brauchen den Standardinhalt nicht, wir löschen ihn sofort und schreiben unseren Test.

Vergessen Sie nicht, das Skript mit #language: ru zu starten, damit der Sammler das kyrillische Alphabet erkennt.
Wir schreiben ein kurzes Skript in russischer Sprache. Ich erinnere Sie daran, dass jeder Satz mit den Schlüsselwörtern beginnen sollte: "Wann", "Dann", "Und", das Symbol "*" usw.

Für mein Beispiel habe ich die Yandex-Website genommen, Sie können jede nehmen.



Um zu sehen, welche Schritte der Test enthält, führen wir unser Skript im Terminal aus:

 cept dry-run acceptance check.feature 



Die Schritte des Skripts werden in der Konsole angezeigt, ihre Implementierung ist jedoch noch nicht verfügbar.

Anschließend führen wir einen Befehl aus, der automatisch Vorlagen für die Implementierung unserer Methoden generiert:

 cept gherkin:snippets acceptance 



Alle Namen aus dem Skript, die in Anführungszeichen standen, werden durch Variablen ersetzt: arg.
Wir kopieren sie vom Terminal und fügen sie in die Datei AcceptanceTester.php ein , in der die Methoden für die Arbeit mit Seitenelementen liegen.



Benennen Sie die Methoden in lesbare um, entsprechend ihrer Essenz (optional), und schreiben Sie ihre Implementierung.



Alles ist einfach, aber noch einfacher, wenn Sie in einer intelligenten Entwicklungsumgebung wie Storm arbeiten, die selbst die erforderlichen Befehle aus der Bibliothek anfordert:



Wir entfernen den Überschuss und schreiben die Methoden:

 /** * @When      */ public function step_beingOnMainPage($page) { $this->amOnPage('/'); } /** * @Then    :element */ public function step_seeElement($element) { this->seeElement($element); } /** * @Then    :button */ public function step_clickOnButton($button) { $this->click($button); } /** * @Then    :field  :text */ public function step_fillField($field, $text) { $this->fillField($field, $text); } 

Mal sehen, was passiert ist. Wir starten ein Team, das uns zeigt, welche Methoden (Schritt) wir jetzt implementieren.

 cept gherkin:steps acceptance 



Erfolg!

Die Schritte in der Feature-Datei werden jedoch immer noch nicht als Methoden erkannt.



Damit Storm versteht, was mit den Schritten zu tun ist, führen wir einen Trick mit der Implementierung der Kontextschnittstelle aus dem Namespace Gherkin Context aus.

 namespace Behat\Behat\Context { interface Context {} } 

Wickeln Sie unsere AcceptanceTester-Klasse in einen Namespace und erben Sie von Context

 implements \Behat\Behat\Context\Context 



Jetzt sind alle Schritte der Feature-Datei an ihre Implementierung gebunden:



Damit Webdriver versteht, worauf zu klicken ist und wo gesucht werden muss, müssen Sie die lesbaren Elementnamen und Seitenadressen durch die entsprechenden Locators und URLs ersetzen, die als Argumente in die Methoden fallen.

Dann bekommen wir einen Test des Formulars:



Und du kannst rennen:

 cept run acceptance 



Bestanden

ca. Wenn das Laden der Seitenelemente lange dauert, können Sie die Wartezeit zur gewünschten Methode hinzufügen:

 $this->waitForElementVisible($element); 

Wir kehren zu unserem Testszenario zurück. Wir sind verärgert darüber, dass die gesamte Lesbarkeit des Tests verloren geht, da anstelle der eindeutigen Namen der Elemente und Seiten HTML-Elemente und URLs angezeigt werden.

Wenn wir dies beheben möchten, ist es Zeit, mit der Implementierung des Seitenobjektmusters fortzufahren.

Gehen Sie zu Seitenobjekt


Erstellen Sie im Verzeichnis _support das Seitenverzeichnis, in das wir unsere Klassenseiten einfügen:

 php vendor/bin/codecept generate:pageobject MainPage 

Das erste Seitenobjekt ist die Hauptseite von Yandex. Nennen wir es MainPage. Wir werden die Klasse gleich nennen:



Hier deklarieren wir statische Felder und Methoden, damit sie aufgerufen werden können, ohne ein Objekt zu erstellen.

Da wir in den Konfigurationen von Acceptance.suite.yml bereits die URL der Startseite angegeben haben: yandex.ru , reicht es für die Hauptseite aus, diese anzugeben

 public static $URL = '/'; 

Als nächstes kommt eine Reihe von Seitenelementen. Wir beschreiben Locators für verschiedene Elemente und geben ihnen klare und eindeutige Namen.

Jetzt müssen Sie die Methode getElement hinzufügen, die den Locator mit dem Namen des Elements aus dem Array zurückgibt.

Als Ergebnis haben wir:

 <?php //location: tests/_support/Page/MainPage.php namespace Page; /**   */ class MainPage { public static $URL = '/'; public static $elements = array( ' ' => "//*[@id='wd-wrapper-_afisha']", ' ' => "//*[@data-statlog='afisha.title.link']", ' ' => "//*[@class='weather__icon weather__icon_ovc']|//*[@class='weather__icon weather__icon_skc_d']", ); public static function getElement($name){ return self::$elements[$name]; } } 

Fügen Sie einige Seitenklassen hinzu:

/ ** Poster * /



/ ** Poster - Suchergebnisse * /



Wir kehren zur Klasse AcceptanceTester.php zurück , in der wir unsere Methoden geschrieben haben.
Erstellen wir darin ein Array von PageObject-Klassen, in denen wir den Seiten die Namen zuweisen und ihre Klassennamen im Namespace angeben:

  private $pages = array( " " => "\Page\MainPage", "" => "\Page\AfishaPage", " -  " => "\Page\AfishaResult" ); 

Jedes neue PageObject wird auf ähnliche Weise zu diesem Array hinzugefügt.

Als Nächstes müssen wir das Feld currentPage erstellen, in dem der Link zum PageObject der aktuellen Seite gespeichert wird:

 private $currentPage; 

Jetzt schreiben wir eine Methode. Wenn sie aufgerufen wird, können wir currentPage abrufen und die benötigte PageObject-Klasse initialisieren.

Es ist logisch, einen solchen Schritt mit der Methode "Wenn der Benutzer zur Seite" Seitenname "navigiert" auszuführen. Dann würde der einfachste Weg, die PageObject-Klasse ohne Prüfungen zu initialisieren, folgendermaßen aussehen:

 /** * @When     :page */ public function step_beingOn($page) { //   pageObject $this->currentPage = $this->pages[$page]; } 

Jetzt schreiben wir die Methode getPageElement, mit der wir das Element bzw. seinen Locator von der aktuellen Seite abrufen können:

 private function getPageElement($elementName) { //         $curPage = $this->currentPage; return $curPage::getElement($elementName); } 



Bei bereits implementierten Methoden müssen die Argumente, die wir am Anfang direkt aus dem Feature-Text erhalten haben, durch Elemente von PageObject ersetzt werden, d. H.

 $arg 

wird die Form annehmen

 getPageElement($arg)) 

Dann nehmen unsere Methoden die Form an:

 /** * @When     :page */ public function step_beingOnMainPage($page) { //      pageObject $this->currentPage = $this->pages[$page]; $curPage = $this->currentPage; $this->amOnPage($curPage::$URL); } /** * @Then    :element */ public function step_seeElement($element) { $this->waitForElementVisible($this->getPageElement($element)); $this->seeElement($this->getPageElement($element)); } /** * @Then    :button */ public function step_clickOnButton($button) { $this->click($this->getPageElement($button)); } /** * @Then    :field  :text */ public function step_fillField($field, $text) { $this->fillField($this->getPageElement($field), $text); } /** * @Then      :field */ public function step_deleteText($field) { $this->clearField($this->getPageElement($field)); } 

Eine weitere Methode zum Anzeigen von Suchergebnissen durch Drücken der Eingabetaste wurde hinzugefügt:

 /** * @Then    ENTER */ public function step_keyboardButton() { $this->pressKey('//input',WebDriverKeys::ENTER); } 

Der letzte Schritt ist, wenn alle erforderlichen Methoden und PageObjects beschrieben sind, müssen Sie den Test selbst umgestalten. Fügen Sie die Schritte hinzu, mit denen PageObject beim Aufrufen einer neuen Seite initialisiert wird. Wir haben diesen "* Benutzer ging zur Seite: Seite".

Aus Gründen der Klarheit werde ich noch einige Schritte hinzufügen. Das Ergebnis ist dieser Test:

 #language: ru :     :   .    .   .      " "     " "     " "     " "      ""     " "  " "     ENTER      " -  "     "   "       " "     " "  ""     ENTER 

Ein solches Testszenario ist für jeden Außenstehenden verständlich und lesbar.

Starten Sie!

Um ein detaillierteres Laufergebnis anzuzeigen, können Sie den Befehl verwenden

 cept run acceptance --debug 

Wir schauen uns das Ergebnis an:



Mithilfe des Seitenobjektmusters können Sie daher alle Seitenelemente von Testskripten trennen und in einem separaten Verzeichnis speichern.

Das Projekt selbst finden Sie unter https://github.com/Remneva/ProjectTutorial

Als beginnender Automatisierungsingenieur bin ich Ihnen dankbar, wenn Sie Ihre Ideen teilen und mir vielleicht sagen, wie ich die Projektstruktur so logisch wie möglich transformieren und vereinfachen kann.

Source: https://habr.com/ru/post/de427031/


All Articles