PFZ Workshop - Automatiseren van functionele tests
1. WORKSHOPDAG 27 APRIL 2013
Automatiseren van functionele tests met Behat en Mink
&
2. VOORBEREIDING
Download en installeerVirtualbox (https://www.virtualbox.org/)
Download en installeerVagrant (http://www.vagrantup.com/)
https://github.com/pfznl/wsd13-functionaltesting/
$ git clone https://github.com/pfznl/wsd13-functionaltesting.git
$ cd wsd13-functionaltesting
$ vagrant up
???
Pro鍖t
3. WAT HEB JE NODIG?
Computer/laptop
PHP 5.3
[Java] (als je selenium wilt draaien)
Virtualbox
[Vagrant]
https://github.com/pfznl/wsd13-functionaltesting/
6. HET PROBLEEM
Verschillend beeld op scope/werking van de oplevering
De klant weet soms achteraf pas hoe het systeem precies
gebruikt kan worden
Het is wel mooi, maar zou x toch y kunnen werken?
Bedankt voor de nieuwe feature, maar nu werkt x niet meer
7. HET PROBLEEM
Verschillend beeld op scope/werking van de oplevering
De klant weet soms achteraf pas hoe het systeem precies
gebruikt kan worden
Het is wel mooi, maar zou x toch y kunnen werken?
Bedankt voor de nieuwe feature, maar nu werkt x niet meer
8. WANT EIGENLIJK...
... hebben we een gezamenlijk doel
... willen we wat we gaan bouwen zo nauwkeurig mogelijk
de鍖ni谷ren
... willen we constante kwaliteit leveren
11. DAT BETEKENT DAT...
... we beter, en op gelijk niveau moeten communiceren
... we een documentatiesysteem moeten bedenken voor de
afstemming
... we regelmatig moeten valideren dat wat we leveren nog
volgens speci鍖caties is
12. DAT BETEKENT DAT...
... we beter, en op gelijk niveau moeten communiceren
... we een documentatiesysteem moeten bedenken voor de
afstemming
... we regelmatig moeten valideren dat wat we leveren nog
volgens speci鍖caties is
Automatiseren?
23. AGILE MANIFESTO
Mensen en hun onderlinge interactie > processen and tools
Werkende software > allesomvattende documentatie
Samenwerking met de klant > contractonderhandelingen
Inspelen op verandering > het volgen van een plan
28. OPSTELLENVAN REQUIREMENTS
Omschrijven hoe een probleem opgelost wordt
Gezamenlijk met de klant opstellen
In de taal (technische) van de klant
Functioneel gericht, niet technisch gericht
29. Ik wil graag een zoekmachine bouwen, zodat
mijn bezoekers het hele internet kunnen
doorzoeken!
30. Ik wil graag een zoekmachine bouwen, zodat
mijn bezoekers het hele internet kunnen
doorzoeken!
Wow, leuke klus!
Ik ga direct aan de slag
32. Alstublieft! Hier is de zoekmachine, mooi h竪?
Jawel, mooie foto op de achtergrond. En
het zoeken werkt, maar ik bedoelde ook
dat bezoekers op afbeeldingen kunnen
zoeken!
33. Alstublieft! Hier is de zoekmachine, mooi h竪?
Jawel, mooie foto op de achtergrond. En
het zoeken werkt, maar ik bedoelde ook
dat bezoekers op afbeeldingen kunnen
zoeken!
Ja zeg, daar kom je nu mee
34. Alstublieft! Hier is de zoekmachine, mooi h竪?
Jawel, mooie foto op de achtergrond. En
het zoeken werkt, maar ik bedoelde ook
dat bezoekers op afbeeldingen kunnen
zoeken!
Ja zeg, daar kom je nu mee
37. PRAKTIJK
Feature: {feature omschrijving}
{intentie}
As a {personage(s)}
I want {feature}
So that {intentie}
Scenario: {scenario omschrijving}
Given {context}
And {meer context}
When {actie}
Then {resultaat}
Scenario: ...
Informatie: http://dannorth.net/whats-in-a-story/
38. VOORBEELD ZOEKFUNCTIE
Feature: Search on the internet
As a bing.com visitor
I want to use the search engine
So that i can find information on the internet
Scenario: Simple keyword search
Given I am on the homepage
When I search the term PHP
Then I should see search results containing PHP
39. LEVENDE DOCUMENTATIE
Alle features en scenarios bij elkaar zijn de documentatie
Bij een change request werk je deze documentatie daarom bij
57. INSTALLERENVAN BEHAT
1. Maak een map genaamd testsuite
2. Installeer composer (http://docs.behat.org/quick_intro.html#installation)
3. Maak een bestand composer.json met de volgende inhoud:
4. $ php composer.phar install
{
"require": {
"behat/behat": "2.4.*@stable"
},
"minimum-stability": "dev",
"config": {
"bin-dir": "bin/"
}
}
$ curl http://getcomposer.org/installer | php
58. INSTALLERENVAN BEHAT
1. Maak een map genaamd testsuite
2. Installeer composer (http://docs.behat.org/quick_intro.html#installation)
3. Maak een bestand composer.json met de volgende inhoud:
4. $ php composer.phar install
{
"require": {
"behat/behat": "2.4.*@stable"
},
"minimum-stability": "dev",
"config": {
"bin-dir": "bin/"
}
}
Thats it!
$ curl http://getcomposer.org/installer | php
59. HELLO BEHAT
1. Initialiseer een Behat testsuite met het commando:
$ bin/behat --init
2. Behat heeft de volgende mappen en bestanden aangemaakt:
3. Run de testsuite: $ bin/behat
60. STEPS? (TERMINOLOGIE)
Feature: {feature omschrijving}
{intentie}
As a {personage(s)}
I want {feature}
So that {intentie}
Scenario: {scenario omschrijving}
Given {context}
And {meer context}
When {actie}
Then {resultaat}
Scenario: ...
61. STEPS? (TERMINOLOGIE)
Feature: {feature omschrijving}
{intentie}
As a {personage(s)}
I want {feature}
So that {intentie}
Scenario: {scenario omschrijving}
Given {context}
And {meer context}
When {actie}
Then {resultaat}
Scenario: ...
Feature, user story, module
62. STEPS? (TERMINOLOGIE)
Feature: {feature omschrijving}
{intentie}
As a {personage(s)}
I want {feature}
So that {intentie}
Scenario: {scenario omschrijving}
Given {context}
And {meer context}
When {actie}
Then {resultaat}
Scenario: ...
Feature, user story, module
Scenario
63. STEPS? (TERMINOLOGIE)
Feature: {feature omschrijving}
{intentie}
As a {personage(s)}
I want {feature}
So that {intentie}
Scenario: {scenario omschrijving}
Given {context}
And {meer context}
When {actie}
Then {resultaat}
Scenario: ...
Feature, user story, module
Scenario
Steps
66. VOORBEELD ZOEKFUNCTIE
Feature: Search on the internet
As a bing.com visitor
I want to use the search engine
So that i can find information on the internet
Scenario: Simple keyword search
Given I am on the homepage
When I search the term PHP
Then I should see search results containing PHP
features/search.feature
67. STEP DEFINITIES
/**
* @Given /^I am on the homepage$/
*/
public function iAmOnTheHomepage()
{
throw new PendingException();
}
/**
* @When /^I search the term "([^"]*)"$/
*/
public function iSearchTheTerm($arg1)
{
throw new PendingException();
}
Deze kun je in FeatureContext.php plaatsen, echter...
68. MINKEXTENSION
1. Is een set van (basis) voorgede鍖nieerde steps
2. Maakt gebruik van Mink
3. Maar... niet alle teksten van de steps zijn even bruikbaar
/**
* Opens homepage.
*
* @Given /^(?:|I )am on (?:|the )homepage$/
* @When /^(?:|I )go to (?:|the )homepage$/
*/
public function iAmOnHomepage()
{
$this->getSession()->visit($this->locatePath('/'));
}
69. MINKTERMINOLOGIE
Driver = Browser controller/emulator
Session = Browser
Page = Document(Element)
Element
Selectors
XPath
CSS
Named
71. MINK STEPS
Given /^(?:|I )am on (?:|the )homepage$/
When /^(?:|I )go to (?:|the )homepage$/
Given /^(?:|I )am on "(?P<page>[^"]+)"$/
When /^(?:|I )fill in "(?P<field>(?:[^"]|")*)" with "(?P<value>(?:[^"]|")*)"$/
When /^(?:|I )press "(?P<button>(?:[^"]|")*)"$/
When /^(?:|I )follow "(?P<link>(?:[^"]|")*)"$/
Then /^(?:|I )should be on "(?P<page>[^"]+)"$/
Then /^(?:|I )should see "(?P<text>(?:[^"]|")*)"$/
$ bin/behat -dl
/**
* Clicks link with specified id|title|alt|text.
*
* @When /^(?:|I )follow "(?P<link>(?:[^"]|")*)"$/
*/
public function clickLink($link)
{
$link = $this->fixStepArgument($link);
$this->getSession()->getPage()->clickLink($link);
}
73. STEP DEFINITIES MAKEN
/**
* @When /^I search the term "([^"]*)"$/
*/
public function iSearchTheTerm($searchTerm)
{
$this->fillField('q', $searchTerm); // Mink definities
$this->pressButton('go');
}
/**
* @When /^I search the term "([^"]*)"$/
*/
public function iSearchTheTerm($searchTerm)
{
$page = $this->getSession()->getPage();
$page->fillField('q', $searchTerm);
$page->pressButton('go');
}
76. DE LAATSTE STAP
Then I should see search results containing PHP
/**
* @Then /^I should see search results containing (.*)$/
*/
public function iShouldSeeSearchResultsContaining($searchTerm)
{
$searchTerm = quotemeta($searchTerm);
$regex = sprintf('/%s/mi', $searchTerm);
$page = $this->getSession()->getPage();
$elements = $page->findAll('xpath', './/div[@class="sa_mc"]');
foreach ($elements as $element) {
if (!preg_match($regex, $element->getText())) {
throw new Exception('One of the elements did not match the searchterm');
}
}
}
77. PROFIELEN
Volledige con鍖guratie per omgeving
Selectie op basis van 鍖lters
Te de鍖nieren in behat.yml:
Aan te roepen met: $ bin/behat --profile profielnaam
default:
extension:
BehatMinkExtensionContextMinkContext
base_url: http://www.example.org
profielnaam:
extension:
BehatMinkExtensionContextMinkContext
base_url: http://acc.example.org
78. TAGS
Handig om selectie te maken van features/scenarios
Via command line: $ bin/behat --tags @slow
De鍖nieer als 鍖lters in pro鍖le
@smoke
Feature: Search on the www
@slow
Scenario: ...
search.feature
slowonly:
filters:
tags: @slow
behat.yml
fast:
filters:
tags: ~@slow
behat.yml
79. FILTERS
Tests groeperen
Snel vs. traag
Tags
Con鍖guratie in behat.yml
smoketests:
filters:
tags: @smoketest&&~@wip
development:
filters:
tags: ~@slow&&~@wip
82. BROWSER CONTROLLERS
Javascript Snelheid Opmerking
Goutte Nee ++ Emulator
Selenium2 Ja -
Sahi Ja ~
Geen response
statuscode, headers,
authenticatie
Zombie.js Ja +
83. SCENARIO OUTLINES
Scenario Outline: Simple keyword search
Given I am on the homepage
When I search the term <searchterm>
Then I should see search results containing <searchterm>
Examples:
| searchterm |
| PHP |
| Java |
| Pie |
| This string is possibly too long and uncommon |
85. MULTILINEVARIABLES
Scenario:
Given I input that spans several lines
"""
Test one
two three
"""
/**
* @Given /^I input that spans several lines$/
*/
public function iInputThatSpansSeveralLines(PyStringNode $string)
{
(string) $string;
$string->getRaw(); // string
$string->getLines(); // array
}
86. TABLES
Scenario:
Given the following users are registered
| name | password |
| Foo | test123 |
| Admin | secret |
When I go to the user overview
Then I should see Foo
And I should see Admin
/**
* @Given /^the following users are registered$/
*/
public function theFollowingUsersAreRegistered(TableNode $table)
{
foreach ($table->getHash() as $row) {
// ... $row['name'], $row['password']
}
}