E2E Tests mit PHP

Preview:

Citation preview

E2E Tests mit PHPWartbare Automatisierte Tests mit PhpUnit, Behat, Mink und dem Page Object Pattern

Lukas Sadzik
Ein Codebeispiel sagt mehr als 1000 Worte ;) (Wie sieht das aus, wenn sich funktionale (SFWebTestCase) Tests PageObjects mit E2E Test teilen?)
Khair-ed-Din Husseini
Hier kommen noch ein paar slides dazu.Evtl. lagert Jakub Z. die PageObjekte von der Behat Extension im Dezember, damit wollte ich dann anfangen den Pattern auf die WebTestCases zu übertragen.Dazu hier mehr: https://github.com/sensiolabs/BehatPageObjectExtension/issues/61Aber auch wenn es zeitlich nicht hin haut, wollte ich ein Beispiel schreiben, in dem ich die Extension in composer reinhole und nur den Pattern und die Factories benutze
Lukas Sadzik
Macht es Sinn, ein Beispiel für die behat.yml zu zeigen? (Wenns sehr einfach ist, demonstriert man, dass es einfach ist, wenns nicht trivial ist, kann man Fallstricke zeigen)

Khair-ed-Din “Mozzy” HusseiniSoftware DeveloperSensioLabs Deutschland

Was sind E2E-Tests nicht?

Frontend Tests -> Domäne des Frontend TeamsDarstellung Tests -> Domäne des Design Teams

Was sind E2E Tests dann?

Tests, welche das Zusammenspiel von User Interface und dem hinterliegenden System in einem Workflow sicherstellen

Wann werden E2E-Tests eingesetzt?

UI

Unit

Manuelle Tests

Service

-> Units sind Grundsteine der Pyramide (Methoden, Funktionen)-> “Viele Units” bedeutet hohe Fehleranfälligkeit-> Units werden isoliert getestet-> Gemockte Abhängigkeiten-> I/O Operationen in Memory

UI

Unit

Manuelle Tests

Service

Unit Layer

-> Integrations und Funktionale bzw. Komponenten Tests-> Komponenten/Funktionale Tests: -> Testen isolierte Komponenten -> Externe Abhängigkeiten sind gemockt-> Integrations Tests:-> Testen workflows zwischen Services und Komponenten

UI

Unit

Manuelle Tests

Service

Service Layer

-> E2E-Tests, die durch das UI durchgeführt werden-> Alle Endpunkte und Komponentent sind voll initialisiert -> Keine Mocks werden verwendet

UI

Unit

Manuelle Tests

Service

UI Layer

Ambiguität aka Wischiwaschi

-> Layer Grenzen sind nicht klar-> Testdefinitionen variieren von Firma zu Firma

UI

Unit

Manuelle Tests

Service

Recap

-> E2E Tests ergänzen Unit, Funktionale und Integrations Tests-> Sie decken die Fälle ab, an denen die anderen Layer nicht rankommen, ohne alle Endpunkte in der Ausführung zu initialisieren

UI

Unit

Manuelle Tests

Service

Womit werden E2E-Tests geschrieben?

Verschiedene Tools verfügbar

PhpUnit, Behat, , Codeception, Atoum, Kahlan, Peridot, SimpleTest, QA-Tools etc...

Behat (BDD)

1. Features und Szenarien werden von Stakeholdern definiert2. Entwickler “übersetzen” die Szenarien in einem bestimmten

Kontext3. Komponenten werden gemockt um die Szenarien des Systems

zu simulieren4. Sukzessive Implementierung der gemockten Komponenten

Behat Szenario BeispieleScenario: Register User Success Given I am on "/register" When I fill in the following: | Username | john | | Email | john@example.com | | Password | mypassword | | Repeat Password | mypassword |

And I check "accept_tou" And I press "register_button" Then I should be on "/register/success"

Scenario: Bad Registration Given I load test data configured in "site_data.yml" And I am on "/register" When I fill in "username" with "john" And I fill in "email" with "john@example.com" And I fill in "password" with "mypassword" And I fill in "password_repeat" with "mypassword" And I check "accept_tou" And I press "register_button" Then I should be on "/register/success" And I should see "Registration Succeeded" in the "#content h1" element

10 steps

-> Testframework Instanz der xUnit Architektur-> Bietet Tools und Bibliotheken für alle möglichen Arten von Tests an-> E2E-Tests werden durch Webdriver u.ä. ermöglicht (z.B. Mink, Facebook Webdriver)

PhpUnit

public function testRegistrationSuccess(){ $driver = new SeleniumDriver(); $session = new Session($driver);

$session->visit('http://my-project.dev/register');

$page = $session->getPage(); $registrationForm = $page->find('css', 'form.register');

$registrationForm->findField('username')->setValue('john'); $registrationForm->findField('email')->setValue('john@example.com'); $registrationForm->findField('password')->setValue('john'); $registrationForm->findField('password_repeat')->setValue('john'); $registrationForm->findField('accept_tou')->check();

$registrationForm->submit(); $page = $session->getPage(); $currentUrl = $session->getCurrentUrl(); $h1 = $page->find('css', '#content h1');

$this->assertEquals('http://my-project.dev/register/success', $currentUrl); $this->assertEquals('Registration Succeeded', $h1->getText());

$session->stop();}

Probleme bei der Implementierung der E2E-Tests

-> Implementationsdetails sind in den Vordergrund geschoben-> Behat Szenario nicht ganz vom Stakeholder verstanden-> Testfälle sind aufgebläht-> Tests werden mit der Zeit schwer wartbar-> Frustrationen führen zur Häufung von manuellen Tests

Page Object Pattern als mögliche Lösung-> UI Abstrations Layer-> Entkoppelt UI Verhalten vom eigentlichen Test-> Entkoppelt WebDriver Implementierung vom eigentlichen Test

Behat & Page Object Pattern

-> (+) Szenario kann von Stakeholdern definiert werden-> (+)Aus 5 Steps werden 3-> (-) Stepdefinitionen müssen geschrieben werden

Scenario: Ok RegistrationGiven I visited the registration pageWhen I register with valid data for JohnThen I should be on the registration success page

PHPUnit & Page Object Pattern

-> (+) Szenario kann von Stakeholdern definiert werden-> (+)Aus 5 Steps werden 3-> (-) Stepdefinitionen müssen geschrieben werden

public function testRegistrationSuccess(){

$driver = new SeleniumDriver();$session = new Session($driver);$page = new RegistrationPage($session);

$page->open();$page->verifyUrl();$newPage = $page->register('john', 'john@example.com', 'mypassword', 'mypassword', true);

$page = new RegistrationSuccessPage($session);$this->assertEquals($page->getUrl(), $newPage->getUrl());/** or **/$page->verifyUrl();$session->stop();}

SensioLabs BehatPageObjectExtension-> Behat Integration-> Page und Element Factory-> Konfigurierbare Namespaces-> https://github.com/sensiolabs/BehatPageObjectExtension-> Jakub Zalas https://www.github.com/jazal

BehatPageObjectExtension & Symfony-> Factories können in Symfony DIC integriert werden-> BrowserKit kann als Mink-Driver verwendet werden-> Page Objekte können mit Funktionalen Tests geteilt werden

-> Da ist schon was vorbereitet :) https://github.com/mozzymoz/e2eTests

Blackfire Player

-> https://blackfire.io/docs/player/index -> Vereinfachte Testdefinitionen durch YAML Syntax-> Szenarien können auch direkt in PHP definiert und verwendet werden-> Szenarien können wiederverwendet werden

options: endpoint: "http://gitlist.demo.blackfire.io/" title: "GitList Scenario" variables: query: foosteps: - title: Search expect: - "status_code() == 200" - "css('table.tree td').count() > 10" - "not (body() matches '/No results found./')" params: query: query submit: css('.form-search')

Blackfire Player

Recap-> POP kapselt viel vom eigentlichen Test ab-> Tests werden durch Entkoppelung stabiler-> Neuer Layer bedeuted ein wenig mehr Code zu schreiben-> Umsetzung kann noch vereinfacht werden-> Vor allem KISS

Q&A

Scenario: Worse eva Registration Given I load test data configured in "site_data.yml" And I am on "/register" When I enter "John" in xpath ".//*[@id='register_form']/div/div[1]/div/input[@name='username']" Then the entered username should be "John" When I enter "john.doe@example.com" in xpath ".//*[@id='content']/div/div[1]/div/input[@name='email']" Then the entered email should be "john.doe@example.com" When I enter "mypassword" in xpath ".//*[@id='register_form']/div/div[1]/div/input[@name='password']" Then the entered password should be "mypassword" When I enter "mypassword" in xpath ".//*[@id='register_form']/div/div[1]/div/input[@name='password_repeat']" Then the entered repeated password should be "mypassword" When I click element in xpath ".//*[@id='register_form']/div/div[1]/div/input[@name='accept_tou']" Then the terms of use should be "accepted" When I click element in xpath ".//*[@id='register_form']/div/div[1]/div/input[@name='register_button']" Then I should be on "/register/success" And I should see the text "Registration Succeeded" in xpath ".//*[@id='content']/h1"

15 steps!!!

Recommended