E2E Tests mit PHP
Wartbare Automatisierte Tests mit PhpUnit, Behat,
Mink und dem Page Object Pattern
Khair-ed-Din “Mozzy” Husseini
Software Developer
SensioLabs Deutschland
Was sind E2E-Tests nicht?
Frontend Tests -> Domäne des Frontend Teams
Darstellung 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 definiert
2. Entwickler “übersetzen” die Szenarien in einem bestimmten Kontext
3. Komponenten werden gemockt um die Szenarien des Systems zu
simulieren
4. Sukzessive Implementierung der gemockten Komponenten
Behat Szenario Beispiele
Scenario: 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 Registration
Given I visited the registration page
When I register with valid data for John
Then 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: foo
steps:
- 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!!!

E2E Tests mit PHP

  • 1.
    E2E Tests mitPHP Wartbare Automatisierte Tests mit PhpUnit, Behat, Mink und dem Page Object Pattern
  • 2.
    Khair-ed-Din “Mozzy” Husseini SoftwareDeveloper SensioLabs Deutschland
  • 3.
    Was sind E2E-Testsnicht? Frontend Tests -> Domäne des Frontend Teams Darstellung Tests -> Domäne des Design Teams
  • 4.
    Was sind E2ETests dann? Tests, welche das Zusammenspiel von User Interface und dem hinterliegenden System in einem Workflow sicherstellen
  • 5.
    Wann werden E2E-Testseingesetzt? UI Unit Manuelle Tests Service
  • 6.
    -> Units sindGrundsteine 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
  • 7.
    -> Integrations undFunktionale 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
  • 8.
    -> E2E-Tests, diedurch das UI durchgeführt werden -> Alle Endpunkte und Komponentent sind voll initialisiert -> Keine Mocks werden verwendet UI Unit Manuelle Tests Service UI Layer
  • 9.
    Ambiguität aka Wischiwaschi ->Layer Grenzen sind nicht klar -> Testdefinitionen variieren von Firma zu Firma UI Unit Manuelle Tests Service
  • 10.
    Recap -> E2E Testsergä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
  • 11.
    Womit werden E2E-Testsgeschrieben? Verschiedene Tools verfügbar PhpUnit, Behat, , Codeception, Atoum, Kahlan, Peridot, SimpleTest, QA-Tools etc...
  • 12.
    Behat (BDD) 1. Featuresund Szenarien werden von Stakeholdern definiert 2. Entwickler “übersetzen” die Szenarien in einem bestimmten Kontext 3. Komponenten werden gemockt um die Szenarien des Systems zu simulieren 4. Sukzessive Implementierung der gemockten Komponenten
  • 13.
    Behat Szenario Beispiele Scenario: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"
  • 14.
    Scenario: Bad Registration GivenI 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
  • 15.
    -> Testframework Instanzder 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
  • 16.
    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(); }
  • 17.
    Probleme bei derImplementierung 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
  • 18.
    Page Object Patternals mögliche Lösung -> UI Abstrations Layer -> Entkoppelt UI Verhalten vom eigentlichen Test -> Entkoppelt WebDriver Implementierung vom eigentlichen Test
  • 20.
    Behat & PageObject Pattern -> (+) Szenario kann von Stakeholdern definiert werden -> (+)Aus 5 Steps werden 3 -> (-) Stepdefinitionen müssen geschrieben werden Scenario: Ok Registration Given I visited the registration page When I register with valid data for John Then I should be on the registration success page
  • 21.
    PHPUnit & PageObject 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(); }
  • 22.
    SensioLabs BehatPageObjectExtension -> BehatIntegration -> Page und Element Factory -> Konfigurierbare Namespaces -> https://github.com/sensiolabs/BehatPageObjectExtension -> Jakub Zalas https://www.github.com/jazal
  • 23.
    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
  • 24.
    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
  • 25.
    options: endpoint: "http://gitlist.demo.blackfire.io/" title: "GitListScenario" variables: query: foo steps: - 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
  • 26.
    Recap -> POP kapseltviel 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
  • 28.
  • 29.
    Scenario: Worse evaRegistration 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!!!

Hinweis der Redaktion

  • #4 E2E Tests werden oft mit Frontend und Darstellungstests gleichgestellt -> sind ganz andere Tests Frontendtests -> haben ihre eigenen Unit und Funktionalentests Darstellung Tests -> schwierig und in einigen Fällen unmöglich umzusetzen, je nach dem wieviele verschiedenen Geräte man abdecken möchte
  • #5 End to End heißt hier vom Anfang des Workflows (Systemendpunktübergreifend) zum Ende.
  • #6 Mike Cohn: Succeeding with Agile
  • #7 Dateisystem Operationen: mikey179/vfsStream -> php stream wrapper um file_* funktionen in memory zu halten
  • #8 Blackbox bereich Units->Komponenten->Services
  • #9 -> Wort UI ist irreführend da E2E Tests auch APIs betreffen können -> sollten nicht mehr als 10% aller tests sein
  • #10 Alister Scott -> in his post “Yet another software testing pyramid”1-> only two sections, “Business Facing Tests” and “Technology facing Tests”. Google uses small medium large
  • #28 Early Bird noch bis zum 31.1. verfügbar