3. Desarrollo guiado por comportamiento (BDD)
con Symfony2, Behat y Mink – Carlos Granados
Carlos Granados
4. Desarrollo guiado por comportamiento (BDD)
con Symfony2, Behat y Mink – Carlos Granados
5. Desarrollo guiado por comportamiento (BDD)
con Symfony2, Behat y Mink – Carlos Granados
6. Desarrollo guiado por comportamiento (BDD)
con Symfony2, Behat y Mink – Carlos Granados
7. Desarrollo guiado por comportamiento (BDD)
con Symfony2, Behat y Mink – Carlos Granados
¿Qué es BDD?
• Desarrollo basado en comportamiento
funcionalidad
• Pasar tests != funcionalidad conseguida
• Historias en lenguaje natural y compartido
• Lenguaje definido y automatizable
• Las historias dirigen nuestro desarrollo
• Podemos comprobar la funcionalidad
8. Desarrollo guiado por comportamiento (BDD)
con Symfony2, Behat y Mink – Carlos Granados
Historias (Stories)
• Características (Features)
• As a [role] I want [feature] so that [benefit]
• Escenarios (Scenarios) y pasos (Steps)
• Precondiciones (Given …)
• Acciones (When…)
• Resultados (Then…)
9. Desarrollo guiado por comportamiento (BDD)
con Symfony2, Behat y Mink – Carlos Granados
Feature: Account Holder withdraws cash
As an Account Holder
I want to withdraw cash from an ATM
So that I can get money when the bank is closed
Scenario 1: Account has sufficient funds
Given the account balance is 100€
And the card is valid
And the machine contains enough money
When the Account Holder inserts the card
And the Account Holder requests 20€
Then the ATM should dispense 20€
And the account balance should be 80€
And the card should be returned
Scenario 2: ...
10. Desarrollo guiado por comportamiento (BDD)
con Symfony2, Behat y Mink – Carlos Granados
Gherkin
• El lenguaje de cucumber
• Lenguaje natural y comprensible
• Lenguaje específico y definido
• Lenguaje automatizable
• Similar a YAML
• Ficheros .feature
11. Desarrollo guiado por comportamiento (BDD)
con Symfony2, Behat y Mink – Carlos Granados
Behat
• BDD para php
• Inspirado por cucumber
• Herramienta de línea de comandos
• Disponible en varios idiomas
• Más información en http://behat.org
12. Desarrollo guiado por comportamiento (BDD)
con Symfony2, Behat y Mink – Carlos Granados
#features/atm.feature
Feature: Account Holder withdraws cash
As an Account Holder
I want to withdraw cash from an ATM
So that I can get money when the bank is closed
Scenario: Account has sufficient funds
Given the account balance is 100€
And the card is valid
And the machine contains enough money
When the Account Holder inserts the card
And the Account Holder requests 20€
Then the ATM should dispense 20€
And the account balance should be 80€
And the card should be returned
Scenario: ...
13. Desarrollo guiado por comportamiento (BDD)
con Symfony2, Behat y Mink – Carlos Granados
$ behat
Feature: Account Holder withdraws cash
As an Account Holder
I want to withdraw cash from an ATM
So that I can get money when the bank is closed
Scenario 1: Account has sufficient funds #features/atm.feature:7
Given the account balance is 100€
...
1 scenario ( 1 undefined)
8 steps (8 undefined)
You can implement undefined steps with these code snippets:
/**
* @Given /^the account balance is (d+)€$/
*/
public function theAccountBalanceIs($argument1)
{
throw new PendingException();
}
...
14. Desarrollo guiado por comportamiento (BDD)
con Symfony2, Behat y Mink – Carlos Granados
// features/bootstrap/FeatureContext.php
<?php
use BehatBehatContextBehatContext,
BehatBehatExceptionPendingException;
class FeatureContext extends BehatContext
{
/**
* @Given /^the account balance is (d+)€$/
*/
public function theAccountBalanceIs ($argument1)
{
throw new PendingException ();
}
}
15. Desarrollo guiado por comportamiento (BDD)
con Symfony2, Behat y Mink – Carlos Granados
// features/bootstrap/FeatureContext.php
<?php
use BehatBehatContextBehatContext,
BehatBehatExceptionPendingException;
class FeatureContext extends BehatContext
{
/**
* @Given /^(?:|the )account balance is (d+)€$/
*/
public function setAccountBalance ($balance)
{
$user = $this->getContainer()->getUser();
$account = $user->getAccount();
$account->setBalance($balance);
}
}
16. Desarrollo guiado por comportamiento (BDD)
con Symfony2, Behat y Mink – Carlos Granados
$ behat
Feature: Account Holder withdraws cash
As an Account Holder
I want to withdraw cash from an ATM
So that I can get money when the bank is closed
Scenario 1: Account has sufficient funds #features/atm.feature:7
Given the account balance is 100€ #featureContext::setAccountBalance()
...
1 scenario (1 pased)
8 steps (8 passed)
17. Desarrollo guiado por comportamiento (BDD)
con Symfony2, Behat y Mink – Carlos Granados
/**
* @Then /^(?:|The )account balance should be (d+)€$/
*/
public function checkAccountBalance ($balance)
{
$user = $this->getContainer()->getUser();
$account = $user->getAccount();
if ($account->getBalance()!=$balance) {
throw new Exception(
'Actual balance is '.$account->getBalance().
'€ instead of '.$balance.'€';
);
}
}
18. Desarrollo guiado por comportamiento (BDD)
con Symfony2, Behat y Mink – Carlos Granados
$ behat
Feature: Account Holder withdraws cash
As an Account Holder
I want to withdraw cash from an ATM
So that I can get money when the bank is closed
Scenario 1: Account has sufficient funds #features/atm.feature:7
...
And the account balance should be 80€ #featureContext::checkAccountBalance()
Actual balance is 100€ instead of 80€
And the card should be returned #featureContext::isCardReturned()
...
1 scenario (1 pased)
8 steps (6 passed, 1 skipped, 1 failed)
19. Desarrollo guiado por comportamiento (BDD)
con Symfony2, Behat y Mink – Carlos Granados
require_once 'PHPUnit/Autoload.php';
require_once 'PHPUnit/Framework/Assert/Functions.php';
...
/**
* @Then /^(?:|the )account balance should be (d+)€$/
*/
public function checkAccountBalance ($balance)
{
$user = $this->getContainer()->getUser();
$account = $user->getAccount();
assertEquals($account->getBalance(), $balance);
}
20. Desarrollo guiado por comportamiento (BDD)
con Symfony2, Behat y Mink – Carlos Granados
...
Scenario: Account has insufficient funds
Given the account balance is 10€
And the card is valid
And the machine contains enough money
When the Account Holder inserts the card
And the Account Holder requests 20€
Then the ATM should not dispense any money
And the ATM should print "Insuficient funds"
And the account balance should be 10€
And the card should be returned
Scenario: ...
21. Desarrollo guiado por comportamiento (BDD)
con Symfony2, Behat y Mink – Carlos Granados
Then the account balance should be 20€
/**
* @Then /^the account balance should be (d+)€$/
*/
Then the ATM should print "Insuficient funds"
/**
* @Then /^the ATM should print "([^"]*)"$/
*/
22. Desarrollo guiado por comportamiento (BDD)
con Symfony2, Behat y Mink – Carlos Granados
Scenario: ...
Given the following users exist:
| name | email | phone |
| Aslak | aslak@email.com | 123 |
| Joe | joe@email.com | 234 |
| Bryan | bryan@email.org | 456 |
/**
* @Given /the following users exist:/
*/
public function insertUsers(TableNode $table)
{
$hash = $table->getHash();
foreach ($hash as $row) {
$user = new User($row['name'], $row['email'], $row['phone']);
$this->database->insert($user);
}
}
23. Desarrollo guiado por comportamiento (BDD)
con Symfony2, Behat y Mink – Carlos Granados
Scenario: Eat 5 out of 12
Given there are 12 cucumbers
When I eat 5 cucumbers
Then I should have 7 cucumbers
Scenario: Eat 5 out of 20
Given there are 20 cucumbers
When I eat 5 cucumbers
Then I should have 15 cucumbers
Scenario: Eat 5 out of 5
Given there are 5 cucumbers
When I eat 5 cucumbers
Then I should have 0 cucumbers
24. Desarrollo guiado por comportamiento (BDD)
con Symfony2, Behat y Mink – Carlos Granados
Scenario Outline: Eat cucumbers
Given there are <start> cucumbers
When I eat <eat> cucumbers
Then I should have <left> cucumbers
Examples:
| start | eat | left |
| 12 | 5 | 7 |
| 20 | 5 | 15 |
| 5 | 5 | 0 |
25. Desarrollo guiado por comportamiento (BDD)
con Symfony2, Behat y Mink – Carlos Granados
Background:
Given the following users exist:
| name | email | phone |
| Aslak | aslak@email.com | 123 |
| Joe | joe@email.com | 234 |
| Bryan | bryan@email.org | 456 |
Scenario:...
Scenario:...
Hooks: http://docs.behat.org/guides/3.hooks.html
26. Desarrollo guiado por comportamiento (BDD)
con Symfony2, Behat y Mink – Carlos Granados
/**
* @Then /^there should be no money in the account$/
*/
public function checkEmptyAccount ()
{
return new Then('the account balance should be 0€');
}
/**
* @When /^the user eats and sleeps$/
*/
public function userEatsAndSleeps ()
{
return array(
new When("the user eats"),
new When("the user sleeps"),
);
}
27. Desarrollo guiado por comportamiento (BDD)
con Symfony2, Behat y Mink – Carlos Granados
Behat en Symfony2: BehatBundle
//deps
[gherkin]
git=https://github.com/Behat/Gherkin.git
target=/behat/gherkin
[behat]
git=https://github.com/Behat/Behat.git
target=/behat/behat
[BehatBundle]
git=https://github.com/Behat/BehatBundle.git
target=/bundles/Behat/BehatBundle
28. Desarrollo guiado por comportamiento (BDD)
con Symfony2, Behat y Mink – Carlos Granados
//app/autoload.php
$loader->registerNamespaces(array(
// ..
'BehatGherkin' => __DIR__.'/../vendor/behat/gherkin/src',
'BehatBehat' => __DIR__.'/../vendor/behat/behat/src',
'BehatBehatBundle' => __DIR__.'/../vendor/bundles',
));
//app/AppKernel.php
public function registerBundles()
{
// ..
if ('test' === $this->getEnvironment()) {
$bundles[] = new BehatBehatBundleBehatBundle();
}
}
29. Desarrollo guiado por comportamiento (BDD)
con Symfony2, Behat y Mink – Carlos Granados
$ app/console -e=test behat --init @AcmeDemoBundle
+Acme
+DemoBundle
+...
+Features
+Context
-FeatureContext.php
+...
30. Desarrollo guiado por comportamiento (BDD)
con Symfony2, Behat y Mink – Carlos Granados
//Acme/DemoBundle/Features/Context/FeatureContext.php
<?php
namespace AcmeDemoBundleFeaturesContext;
use BehatBehatBundleContextBehatContext;
class FeatureContext extends BehatContext
{
/**
* @Given /I have a product "([^"]*)"/
*/
public function insertProduct($name)
{
$em = $this->getContainer()->get('doctrine')
->getEntityManager();
$product = new AcmeDemoBundleEntityProduct();
$product->setName($name);
$em->persist($product);
$em->flush();
}
}
31. Desarrollo guiado por comportamiento (BDD)
con Symfony2, Behat y Mink – Carlos Granados
BDD en Symfony2: ejecutar tests
$ app/console –e=test behat @AcmeDemoBundle
32. Desarrollo guiado por comportamiento (BDD)
con Symfony2, Behat y Mink – Carlos Granados
Pruebas de funcionalidad web: Mink
• Librería php integrada con behat
• Permite usar distintos Browser emulators
• Controlar el Navegador
• Recorrer la Página
• Manipular la Página
• Simular la interacción del Usuario
• Interface común para todos los emuladores
33. Desarrollo guiado por comportamiento (BDD)
con Symfony2, Behat y Mink – Carlos Granados
Tipos de Browser emulators:
• Emuladores Headless Browsers
• Symfony Web Client
• Goutte
• Emuladores Browser controllers
• Selenium
• Sahi
• Mixtos: Zombie.js
34. Desarrollo guiado por comportamiento (BDD)
con Symfony2, Behat y Mink – Carlos Granados
Controlar el Navegador
// iniciar driver:
$driver = new BehatMinkDriverGoutteDriver();
// iniciar sesión:
$session = new BehatMinkSession($driver);
// arrancar sesión:
$session->start();
// abrir una página en el navegador:
$session->visit('http://my_project.com/some_page.php');
// obtener el código de respuesta:
echo $session->getStatusCode();
// obtener el contenido de la página:
echo $session->getPage()->getContent();
35. Desarrollo guiado por comportamiento (BDD)
con Symfony2, Behat y Mink – Carlos Granados
// utilizar la historia del navegador:
$session->reload();
$session->back();
$session->forward();
// evaluar expresión Javascript:
echo $session->evaluateScript(
"(function(){ return 'something from browser'; })()"
);
// obtener los headers:
print_r($session->getResponseHeaders());
// guardar cookie:
$session->setCookie('cookie name', 'value');
// obtener cookie:
echo $session->getCookie('cookie name');
36. Desarrollo guiado por comportamiento (BDD)
con Symfony2, Behat y Mink – Carlos Granados
Recorrer la Página: selectors
//xpath selector
$handler = new BehatMinkSelectorSelectorsHandler();
$xpath = $handler->selectorToXpath('xpath', '//html');
//css selector
$selector = new BehatMinkSelectorCssSelector();
$xpath = $selector->translateToXPath('#ID');
//named selectors
$selector = new BehatMinkSelectorNamedSelector();
$xpath = $selector->translateToXPath(
array('field', 'id|name|value|label')
);
//named selectors: link, button, content, select, checkbox
//radio, file, optgroup, option, table
37. Desarrollo guiado por comportamiento (BDD)
con Symfony2, Behat y Mink – Carlos Granados
Recorrer la Página: obtener elementos
//obtengo la página
$page = $session->getPage();
//encuentro un elemento
$element = $page->find('xpath', '//body');
//encuentro todos los elementos
$elementsByCss = $page->findAll('css', '.classname');
//encuentro un elemento por su Id
$element = $page->findById('ID');
//encuentro elementos con named selectors
$link = $page->findLink('href');
$button = $page->findButton('name');
$field = $page->findField('id');
38. Desarrollo guiado por comportamiento (BDD)
con Symfony2, Behat y Mink – Carlos Granados
Manipular la Página: Node Elements
//obtengo un elemento
$el = $page->find('css', '.something');
// obtengo el nombre del tag:
echo $el->getTagName();
// compruebo si tiene un atributo:
$el->hasAttribute('href');
// obtengo un atributo:
echo $el->getAttribute('href');
//obtengo el texto
$plainText = $el->getText();
//obtengo el html
$html = $el->getHtml();
39. Desarrollo guiado por comportamiento (BDD)
con Symfony2, Behat y Mink – Carlos Granados
Manipular la Página: Form fields
// marcar/desmarcar checkbox:
if ($el->isChecked()) {
$el->uncheck();
}
$el->check();
// elegir option en select:
$el->selectOption('optin value');
// añadir un fichero:
$el->attachFile('/path/to/file');
// obtener el valor:
echo $el->getValue();
// poner un valor:
$el->setValue('some val');
40. Desarrollo guiado por comportamiento (BDD)
con Symfony2, Behat y Mink – Carlos Granados
Simular la interacción del Usuario
// pulsar un botón:
$el->press();
//simular el ratón
$el->click();
$el->doubleClick();
$el->rightClick();
$el->mouseOver();
$el->focus();
$el->blur();
//Hacer drag'n'drop
$el1->dragTo($el2);
41. Desarrollo guiado por comportamiento (BDD)
con Symfony2, Behat y Mink – Carlos Granados
Integración con Behat: MinkContext
// features/bootstrap/FeatureContext.php
use BehatMinkBehatContextMinkContext;
class FeatureContext extends MinkContext
{
/**
* @Then /^I press the submit button$/
*/
public function PressSubmitButton()
{
$page = $this->getSession()->getPage();
$button = $page->findButton('submit');
$button->press();
}
}
42. Desarrollo guiado por comportamiento (BDD)
con Symfony2, Behat y Mink – Carlos Granados
Steps predefinidos: Given/When
Given I am on "URL"
When I go to "url"
When I reload the page
When I move backward one page
When I move forward one page
When I press "button"
When I follow "link"
When I fill in "field" with "value"
When I fill in "value" for "field"
When I fill in the following:
When I select "option" from "select"
When I additionally select "option" from "select"
When I check "option"
When I uncheck "option"
When I attach the file "path" to "field"
43. Desarrollo guiado por comportamiento (BDD)
con Symfony2, Behat y Mink – Carlos Granados
Steps predefinidos: Then
Then I should be on "page"
Then the url should match "pattern"
Then the response status code should be "code"
Then the response status code should not be "code"
Then I should see "text"
Then I should not see "text"
Then I should see "text" in the "element" element
Then the "element" element should contain "value"
Then I should see an "element" element
Then I should not see an "element" element
Then the "field" field should contain "value"
Then the "field" field should not contain "value"
Then the "checkbox" checkbox should be checked
Then the "checkbox" checkbox should not be checked
Then I should see "num" "element" elements
Then print last response
44. Desarrollo guiado por comportamiento (BDD)
con Symfony2, Behat y Mink – Carlos Granados
# features/search.feature
Feature: Search
In order to see a word definition
As a website user
I need to be able to search for a word
Scenario: Searching for a page that does exist
Given I am on "/wiki/Main_Page"
When I fill in "search" with "Behavior Driven Development"
And I press "searchButton"
Then I should see "agile software development"
Scenario: Searching for a page that does NOT exist
Given I am on "/wiki/Main_Page"
When I fill in "search" with "Glory Driven Development"
And I press "searchButton"
Then I should see “No results found"
45. Desarrollo guiado por comportamiento (BDD)
con Symfony2, Behat y Mink – Carlos Granados
/**
* @Given /^I am on the main page$/
*/
public function goToMainPage()
{
return new Given('I am on "/wiki/Main_Page"');
}
/**
* @Then /^I press the search button$/
*/
public function pressSearchButton()
{
return new Then('I press "searchButton"');
}
46. Desarrollo guiado por comportamiento (BDD)
con Symfony2, Behat y Mink – Carlos Granados
Mink en Symfony2: MinkBundle
//deps
[mink]
git=https://github.com/Behat/Mink.git
target=/behat/mink
[MinkBundle]
git=https://github.com/Behat/MinkBundle.git
target=/bundles/Behat/MinkBundle
47. Desarrollo guiado por comportamiento (BDD)
con Symfony2, Behat y Mink – Carlos Granados
//app/autoload.php
$loader->registerNamespaces(array(
// ..
'BehatMink' => __DIR__.'/../vendor/behat/mink/src',
'BehatMinkBundle' => __DIR__.'/../vendor/bundles',
));
//app/AppKernel.php
public function registerBundles()
{
// ..
if ('test' === $this->getEnvironment()) {
$bundles[] = new BehatBehatBundleBehatBundle();
$bundles[] = new BehatMinkBundleMinkBundle();
}
}
48. Desarrollo guiado por comportamiento (BDD)
con Symfony2, Behat y Mink – Carlos Granados
#app/config/config_test.yml
mink:
base_url: http://localhost/app_test.php
browser_name: chrome
goutte: ~
sahi: ~
zombie: ~
49. Desarrollo guiado por comportamiento (BDD)
con Symfony2, Behat y Mink – Carlos Granados
//web/app_test.php
if (!in_array(@$_SERVER['REMOTE_ADDR'], array(
'127.0.0.1',
'::1',
))) {
header('HTTP/1.0 403 Forbidden');
exit('You are not allowed to access this file. Check
'.basename(__FILE__).' for more information.');
}
require_once __DIR__.'/../app/bootstrap.php.cache';
require_once __DIR__.'/../app/AppKernel.php';
use SymfonyComponentHttpFoundationRequest;
$kernel = new AppKernel('test', true);
$kernel->loadClassCache();
$kernel->handle(Request::createFromGlobals())->send();
50. Desarrollo guiado por comportamiento (BDD)
con Symfony2, Behat y Mink – Carlos Granados
namespace AcmeDemoBundleFeaturesContext;
use BehatMinkBundleContextMinkContext;
class FeatureContext extends MinkContext
{
/**
* @When /^I go to the user account page$/
*/
public function showUserAccount()
{
$user = $this->getContainer()->get('security.context')
->getToken()->getUser();
$session = $this->getSession();
$session->visit('/account/'. $user->getSlug());
}
}
51. Desarrollo guiado por comportamiento (BDD)
con Symfony2, Behat y Mink – Carlos Granados
Qué Driver usar
# symfony driver (default)
@mink:symfony
Scenario: ...
# goutte driver
@mink:goutte
Scenario: ...
# sahi driver
@mink:sahi o @javascript
Scenario: ...
# zombie.js driver
@mink:zombie
Scenario: ...
52. Desarrollo guiado por comportamiento (BDD)
con Symfony2, Behat y Mink – Carlos Granados
Trucos: Salida de Behat
app/console -e=test behat -f pretty,junit --out ,. @AcmeDemoBundle
53. Desarrollo guiado por comportamiento (BDD)
con Symfony2, Behat y Mink – Carlos Granados
Trucos: repetir Tests
app/console -e=test behat --rerun="re.run" @AcmeDemoBundle
54. Desarrollo guiado por comportamiento (BDD)
con Symfony2, Behat y Mink – Carlos Granados
BDD vs TDD
• BDD es TDD
BDD vs UnitTesting
• Unit testing comprueba unidades
• BDD comprueba funcionalidad
55. Desarrollo guiado por comportamiento (BDD)
con Symfony2, Behat y Mink – Carlos Granados
Stop Press!!! Behat 2.4
• BehatBundle y MinkBundle deprecated
• Usar MinkExtension y Symfony2Extension
• Más info en http://behat.org
56. Desarrollo guiado por comportamiento (BDD)
con Symfony2, Behat y Mink – Carlos Granados
//deps
[mink]
git=https://github.com/Behat/Mink.git
target=/behat/mink
version=v1.3.3
[gherkin]
git=https://github.com/Behat/Gherkin.git
target=/behat/gherkin
version=v2.1.1
[behat]
git=https://github.com/Behat/Behat.git
target=/behat/behat
version=v2.3.5
57. Desarrollo guiado por comportamiento (BDD)
con Symfony2, Behat y Mink – Carlos Granados
¡¡Gracias!!
¿Preguntas?
• carlos@planetapluton.com
• @carlos_granados
• http://es.linkedin.com/in/carlosgranados