Mink & Behat
BDD - Behaviour Driven Development
Flujo BDD
Stakeholders:
trabajadores, propietarios,
proveedores…
(cualquiera con alguna
relación con la empresa)
Es un proceso de desarrollo de software
Un tipo de TDD... pero más verboso
donde intervienen varios colaboradores (stakeholders)
¿Qué es BDD?
HISTORIA
Tipos de BDD
ESPECIFICACIONES
STORY SPEC
Tipos de BDD
Story BDD* se lleva a cabo con Behat
Enfocado en la narrativa = QUÉ
Tipos de BDD
Spec BDD se lleva a cabo con PHPSpec
Enfocado en la implementación = CÓMO
Mink & Behat
Qué es
Proyecto Open Source creado por Konstantin Kudryashov
Desarrollo albergado en GitHub: https://github.com/Behat/Behat
¿Qué es Behat?
¿Qué es Behat?
Un framework para testing en BDD (Behaviour Driven Development)
Escrito en php y para php 5.3+
Inspirado en Cucumber, de Ruby (https://cucumber.io/)
Behat 3 está considerado como la versión oficial de la implementación de
Cucumber para php y muy bien considerado en el mundo del BDD
¿Qué es Behat?
Behat usa como lenguaje para contar historias: Gherkin
Gherkin es un DSL, Domain Specific Language, como SQL
¿Qué es Behat?
Gherkin usa un lenguaje humano real estructurado
Uno modo de escribir una historia que puede ser automatizada
¿Qué es Behat?
Las sentencias se pueden escribir en varios idiomas
NO es un lenguaje de programación
¿Qué es Behat?
Promueve la comunicación entre todas las partes que intervienen en la empresa,
como por ejemplo: la parte de negocio y la parte de desarrollo
¿Por qué Behat?
Ubiquitous Language (Martin Fowler): https://martinfowler.com/bliki/UbiquitousLanguage.html
UBIQUITOUS LANGUAGE
Feature: The Coffee Machine serves coffees
In order to take a coffee
As a buyer
I should be able to buy a coffee to stay awake
Scenario: Buy one coffee
Given Coffee Machine can serve up to 10 coffees at $0.60
When I deposit 1 dollar
And I press the coffee button
Then I should be served a coffee
And I should be returned $0.40
And There should be 9 coffees left
Vamos a contar una historia
Feature: The Coffee Machine serves coffees
In order to take a coffee
As a buyer
I should be able to buy a coffee to stay awake
Scenario: Buy one coffee
Given Coffee Machine can serve up to 10 coffees at $0.60
When I deposit 1 dollar
And I press the coffee button
Then I should be served a coffee
And I should be returned $0.40
And There should be 9 coffees left
QUÉ
POR QUÉ
QUIEN
rol de usuario
Feature: The Coffee Machine serves coffees
In order to take a coffee
As a buyer
I should be able to buy a coffee to stay awake
Scenario: Buy one coffee
Given Coffee Machine can serve up to 10 coffees at $0.60
When I deposit 1 dollar
And I press the coffee button
Then I should be served a coffee
And I should be returned $0.40
And There should be 9 coffees left
1 única Feature (Historia) por archivo .feature
N Scenarios por Feature
Feature: The Coffee Machine serves coffees
In order to take a coffee
As a buyer
I should be able to buy a coffee to stay awake
Scenario: Buy one coffee
Given Coffee Machine can serve up to 10 coffees at $0.60
When I deposit 1 dollar
And I press the coffee button
Then I should be served a coffee
And I should be returned $0.40
And There should be 9 coffees left
Sentences:
Acciones/Steps
a testear
Feature: The Coffee Machine serves coffees
In order to take a coffee
As a buyer
I should be able to buy a coffee to stay awake
Scenario: Buy one coffee
Given Coffee Machine can serve up to 10 coffees at $0.60
When I deposit 1 dollar
And I press the coffee button
Then I should be served a coffee
And I should be returned $0.40
And There should be 9 coffees left
precondiciones/
inicializaciones
interacción
de usuario
resultados
¿Dónde ocurre la magia?
¿Y dónde está la implementación de los tests?
Behat “mapea” definiciones Gherkin con implementación Php y
las relaciona mediante anotaciones
Scenario Steps
.feature
acciones en Gherkin
Step Definitions
FeatureContext.php
Código PHP
/**
* @When I deposit 1 dollar
*/
public function iDepositOneDollar()
{
// do something
}
Feature: The Coffee Machine serves coffees
...
Scenario: Buy one coffee
Given ...
When I deposit 1 dollar
And ...
gherkin
.feature
FeatureContext.php
anotación + método
1.- Instalar composer
2.- Crear archivo composer.json
3.- Ejecutar composer install
{
"require-dev": {
"behat/behat": "3.3.1"
},
"config": {
"bin-dir": "bin/"
}
}
Configuración proyecto básico Behat
4.- Inicializar Behat para configurar un framework base: ./bin/behat --init
5.- Tras crear los tests ejecutar ./bin/behat
6.- Opcionalmente crear archivo behat.yml (configuración avanzada)
bin
composer.json
composer.lock
features
|_ bootstrap
|_ FeatureContext.php
vendor ...
Ubicación para los
archivos .feature
Ubicación archivos
*Context.php
Configuración proyecto básico Behat
Una puede definir uno o más Scenario
Cada testea un comportamiento
Cada puede definir uno o más Steps
Los Steps se pueden encadenar
Los Comentarios están permitidos
Given When And/But Then
Scenario
Scenario
Feature
# this is a comment
Qué define cada Keyword
Scenario Outline
Background
Given ( + table
transformation )
Examples ( + table )
Más definiciones de Keywords
Se ejecuta el mismo
Scenario para cada fila
de la tabla
Todos los Steps dentro
de Background se
iniciarán antes de cada
Scenario
Nos permite inicializar
datos para trabajar
dentro de un Scenario
Escritura de una Feature: GOOD PRACTICE
Explica QUÉ es lo que se desea hacer y NO CÓMO de modo que
todo el mundo pueda entender la situación
Pensar 2 veces antes de escribir una frase de modo que
cualquiera pueda entender el caso *
* caso usb
Indicar expresamente el valor de un elemento de formulario
Utilizar en una sentencia el valor de un id
Bad: I press the button with id “server-coffee”
Escritura de una Feature: BAD PRACTICE
Good: I press the coffee button
Escritura de una Feature: GOOD PRACTICE
Indicar cuál es la acción que se desea llevar a cabo, no cómo
Mink & Behat
Qué es
¿Qué es Mink?
Mink es un proyecto Open Source para PHP5.3+ que permite
controlar/simular un navegador web
y poder así testear aplicaciones web
El desarrollo está hospedado en GitHub: https://github.com/minkphp/Mink
Desarrollado y mantenido por varios contribuidores
¿Qué es Mink?
Con Mink es posible
Controlar el Navegador Recorrer un página
Manipular elementos
de un página
Interaccionar con
una página
Controlar el Navegador
Ej: $this->getSession()->visit(‘http://web-site.ext’);
¿Qué permite llevar a cabo Mink?
Recorrer una página
Ej: $this->getSession()->getPage()->find(‘css’, ‘h1’);
¿Qué permite llevar a cabo Mink?
Manipular elementos
de una página
Ej: $this->getSession()->getPage()->getText();
¿Qué permite llevar a cabo Mink?
Interaccionar con una
página
Ej: $this->getSession()->getPage()->findById(‘div-cta’)->click();
¿Qué permite llevar a cabo Mink?
¿Qué es Mink?
Mink provee principalmente 4 objetos
DRIVER SESSION NODE ELEMENTPAGE
DRIVER: $goutteDriver = new GoutteDriver();
SESSION: $this->getSession()
PAGE: $this->getSession()->getPage()
NODEELEMENT: $this->getSession()->getPage()->find(‘h1’)
¿Por qué Mink?
Mink implementa drivers específicos para cada Emulador Web
abstrayendo llamadas API específicas
Goutte Selenium2 Zombie
¿Por qué Mink?
Puede “lanzar navegadores”
Clientes Web con Interfaz gráfica Servidores web sin Interfaz Gráfica
Permite testear distintas resoluciones web
¿Por qué Mink?
Permite testear distintos motores de navegadores web
¿Por qué Mink?
En sistemas operativos diversos
¿Por qué Mink?
Se integra bien con “Navegadores” Web gracias a: Selenium & Sahi
Sahi package abandonado y sin mantener: https://packagist.org/packages/behat/mink-sahi-driver
¿Por qué Mink?
Es posible testear funcionalidades javascript
¿Por qué Mink?
Se puede integrar fácilmente con frameworks para Php tales como
Symfony/Laravel
¿Por qué Mink?
Ofrece un API común para todos los diferentes Emuladores Web
mediante la definición de una interfaz (contrato)
¿Por qué Mink?
2 Tipos de Emuladores Web
1.- Emuladores Web Headless (sin cabeza)
2.- Controladores Web (Browser Controllers)
Son simplemente implementaciones de especificaciones HTTP
Emuladores Web Headless
No se puede testear JS Rápidos (Goutte)
cUrl o Guzzle
Su objetivo es controlar un navegador web real
Controladores Web (Browser Controllers)
SI que puede testear JS! Lento (Selenium/Sahi)
¿Cómo funciona Mink con Selenium?
Selenium2
Chrome
Gecko
En otra terminal
se inicia una
instancia de
Selenium Server
Standalone (.jar)
el cual
Interacciona con
los motores de
navegadores
reales
directamente o vía
drivers específicosMink inicia una
sesión con un
driver específico
Este driver a su
vez se comunica
con la
aplicación/driver
específica del
navegador web
1
2
3
Emulador Web
Headless
Controlador Web
Entonces … ¿cuál es mejor?
Depende del tipo de test
Entonces … ¿cuál es mejor?
Mink & Behat
El binomio y
¿Por qué Behat y Mink?
La ecuación permite testear
el comportamiento de un proceso web completo
El binomio actúa como intermediario
Product Managers y
clientes pueden contar una
historia (mediante un
lenguaje estructurado)
De modo que los
desarrolladores pueden
entender fácilmente los
requerimientos e
implementarlos
¿Por qué Behat y Mink?
Es un modo natural de validar los
requerimientos
¿Por qué Behat y Mink?
Promueve escribir los tests antes de escribir
una línea de código
código de mejor calidad
¿Por qué Behat y Mink?
ayuda a evitar malentendidos
¿Por qué Behat y Mink?
UBIQUITOUS LANGUAGE
Navegando por Wikipedia
Mink & Behat
Ejemplo práctico
Navegando por Wikipedia
Visitar la página de Wikipedia en Inglés
Buscar la definición de Behat
Guardar las referencias en un archivo de texto
¿Qué se pretende
Dependencias (composer.json)
{
"require-dev" : {
"behat/behat": "3.1.1",
"behat/mink": "^1.6",
"behat/mink-extension" : "^2.2",
"behat/mink-goutte-driver" : "^1.2"
},
"config": { "bin-dir": "bin/" }
}
Ejemplo práctico - Scrapper
Biblioteca Mink
Integración entre Behat y Mink
* (incluye MinkContext)
Driver de Mink para Goutte
(package independiente = buena práctica)
Biblioteca Behat
Inicialización del framework Behat: ./bin/behat --init
bin
composer.json
composer.lock
features
|_ bootstrap
|_ FeatureContext.php
vendor ...
Ubicación para los
archivos .feature
Ubicación archivos
*Context.php
Ejemplo práctico - Scrapper
default:
suites:
default:
contexts:
- FeatureContext
- BehatMinkExtensionContextMinkContext
extensions:
BehatMinkExtension :
base_url: http://en.wikipedia.org
goutte: ~
Archivo de configuración behat.yml
MinkContext ofrece una
colección de Step Definitions
gratis!
Mink Extension (pegamento
entre Behat y Mink)
Ejemplo práctico - Scrapper
default | When /^(?:|I )go to (?:|the )homepage$/
default | Then /^(?:|I )should be on "(?P<page>[^"]+)"$/
default | When /^(?:|I )follow "(?P<link>(?:[^"]|")*)"$/
default | When /^(?:|I )press "(?P<button>(?:[^"]|")*)"$/
default | Then /^(?:|I )should see "(?P<text>(?:[^"]|")*)"$/
default | Then /^(?:|I )should not see "(?P<text>(?:[^"]|")*)"$/
default | When /^(?:|I )fill in "(?P<field>(?:[^"]|")*)" with "(?P<value>(?:[^"]|")*)"$/
default | Then /^print current URL$/
default | Then /^print last response$/
default | When /^(?:|I )select "(?P<option>(?:[^"]|")*)" from "(?P<select>(?:[^"]|")*)"$/
default | When /^(?:|I )check "(?P<option>(?:[^"]|")*)"$/
default | Then /^the "(?P<checkbox>(?:[^"]|")*)" checkbox should be checked$/
default | Then /^(?:|I )should see "(?P<text>(?:[^"]|")*)" in the "(?P<element>[^"]*)" element$/
Algunas Step Definitions que nos ofrece gratis Mink vía MinkContext:
$ ./bin/behat -dl
Nota: Otros contextos interesantes: Behatch, StepThroughExtension, PageObjectExtension
Visit "https://www.wikipedia.org"
and click on "English"
Navegando por Wikipedia
I am redirected to "wiki/Main_Page"
I search "behat computer" and click on "Behat (computer science)"
Navegando por Wikipedia
Save references into a text file
Navegando por Wikipedia
Feature: Scrap behat references from wikipedia
In order get behat references
As an anonymous user
I visit wikipedia behat entry and scrap the references list
@javascript
Scenario: Scrap references
Given I am on "https://www.wikipedia.org" #Given I go to homepage
Then I should see "The Free Encyclopedia"
When I follow "English"
Then the url should match "wiki/Main_Page"
And I should see "Welcome to Wikipedia,"
When I fill in "Search Wikipedia" with "behat computer"
And I press "Search Wikipedia"
And print current URL #Debug
And I follow "Behat (computer science)"
Then I should see "Behat is intended to aid communication between"
And I save references in a local storage device#Sentencia a implementar
Archivo .feature: features/wikipedia.scrapper.feature
use BehatMinkExtensionContextRawMinkContext;
class FeatureContext extends RawMinkContext implements Context, SnippetAcceptingContext
/**
* @Given /^I save references in a local storage device$/
*/
public function iSaveReferencesInALocalStorageDevice ()
{
…
}
FeatureContext.php
Requerido para obtener una
Session de Mink
/** @Given /^I save references in a local storage device$/ */
public function iSaveReferencesInALocalStorageDevice ()
{
...
}
Step Definition
propio
Método que guarda las preferencias. Paso a paso
/** @Given /^I save references in a local storage device$/ */
public function iSaveReferencesInALocalStorageDevice ()
{
$page = $this->getSession()->getPage();
...
}
Obtener el
objeto Page
Obtener el objeto
Session (pensar en
una pestaña del
navegador)
Método que guarda las preferencias. Paso a paso
/** @Given /^I save references in a local storage device$/ */
public function iSaveReferencesInALocalStorageDevice ()
{
$page = $this->getSession()->getPage();
$content = $page->find('named', array('id', 'mw-content-text' ));
}
named Selector
buscar por id
Método que guarda las preferencias. Paso a paso
/** @Given /^I save references in a local storage device$/ */
public function iSaveReferencesInALocalStorageDevice ()
{
...
$references = $content->find('css', '.references');
$items = $references->findAll('css', 'li');
}
Selectores CSS
¡Atención con el punto!
find vs findAll
Método que guarda las preferencias. Paso a paso
/** @Given /^I save references in a local storage device$/ */
public function iSaveReferencesInALocalStorageDevice ()
{
...
$links = array();
foreach ($items as $item) {
$linkContainer = $item->find('xpath', '//span[@class="reference-text"]' );
$links[] = $linkContainer ->find('xpath', '//a/@href')->getText();
}
}
Selectores xpath
obtener sólo el valor del enlace
Método que guarda las preferencias. Paso a paso
/** @Given /^I save references in a local storage device$/ */
public function iSaveReferencesInALocalStorageDevice ()
{
...
file_put_contents ('scrapped_references.txt' , join(PHP_EOL, $links));
}
Guardar el archivo en disco
Método que guarda las preferencias. Paso a paso
/** @Given /^I save references in a local storage device$/ */
public function iSaveReferencesInALocalStorageDevice ()
{
$page = $this->getSession()->getPage();
$content = $page->find('named', array('id', 'mw-content-text' ));
$references = $content->find('css', '.references');
$items = $references->findAll('css', 'li');
$links = array();
foreach ($items as $item) {
$linkContainer = $item->find('xpath', '//span[@class="reference-text"]' );
$links[] = $linkContainer ->find('xpath', '//a/@href')->getText();
}
file_put_contents ('scrapped_references.txt' , join(PHP_EOL, $links));
}
2.- Css Selectors
3.- Xpath Selectors
1.- Named Selector
Método que guarda las preferencias. Paso a paso
: implementar la misma solución con menor código
Vamos a Refactorizar
/** @Given /^I save references in a local storage device again$/ */
public function iSaveReferencesInALocalStorageDeviceAgain ()
{
$closure = function($item) { return $item->getText(); }; // anonymous function
$xpath = '//span[@class="reference-text"]/a/@href';
$links = array_map($closure, $this->findAll('xpath', $xpath));
file_put_contents ('scrapped_references_again.txt' , join(PHP_EOL, $links));
}
public function __call($method, $parameters)
{
$page = $this->getSession()->getPage();
if (method_exists($page, $method)) {
return call_user_func_array (array($page, $method), $parameters);
}
}
Magic Call
Sólo un selector xpath
Método que guarda las preferencias. Solución corta
Closure vs Lambda: https://www.ibm.com/developerworks/library/os-php-5.3new2/os-php-5.3new2-pdf.pdf
Ejecutar el test
./bin/behat
bdd# ./bin/behat
Feature: Scrap behat references from wikipedia
...
Scenario: Scrap References # features/wikipedia.scrapper.feature:6
Given I am on " https://www.wikipedia.org" # BehatMinkExtensionContextMinkContext::visit()
Then I should see " The Free Encyclopedia" # Behat...MinkContext::assertPageContainsText()
When I follow " English" # Behat...MinkContext::clickLink()
Then the url should match "wiki/Main_Page" # Behat...MinkContext::assertUrlRegExp()
And I should see " Welcome to Wikipedia," # Behat...MinkContext::assertPageContainsText()
When I fill in " Search Wikipedia" with "behat computer" # Behat...MinkContext::fillField()
And I press " Search Wikipedia" # Behat...MinkContext::pressButton()
And I follow " Behat (computer science)" # Behat...MinkContext::clickLink()
Then I should see " Behat is intended to aid communication between developers, clients and other
stakeholders during a..." # Behat...MinkContext::assertPageContainsText()
And I save references in a local storage device
# FeatureContext::iSaveReferencesInALocalStorageDevice()
1 scenario (1 passed)
10 steps (10 passed)
0m4.15s (15.36Mb)
Bonus track
Echando una mano a Mink
El flujo de ejecución de Behat está basado en eventos
Behat provee 8 Hooks de gran ayuda. Principio DRY
Los eventos se “mapean” mediante anotaciones
Las anotaciones simplifican la gestión de Datafixtures
Behat ofrece tags predefinidos (@javascript) y admite tags personalizados
feature1.feature
Gherkin
Parser
testFeature
testScenario
testStep
feature2.feature
featureN.feature
testBackground
Event
Dispatcher
Listener1
Listener2
ListenerN
...
@BeforeSuite
@BeforeFeature
@BeforeScenario
@BeforeStep
@AfterStep
@AfterScenario
@AfterFeature
@AfterSuite
...
1) Java JRE
2) Firefox, Chrome y/o otros drivers. Ver sección Third Party Drivers, Bindings,
and Plugins at http://www.seleniumhq.org/download)
3) Selenium Standalone Server (http://www.seleniumhq.org/)
4) xvfb (X Virtual Frame Buffer). Permite crear una “gráfica virtual” en memoria.
5) Servidor web integrado por PHP (bin/console server:run) o un Servidor web
con urlRewrite (Apache/nginx) y host debidamente configurado.
Imprescindibles para ejecutar los tests:
Nota: Firefox v.46 con selenium-server-standalone-2.53.1.jar funciona sin problemas
En resumen:
$ ./bin/behat --config ./app/config/behat.yml
$ ./bin/console server:start
$ java -jar ./bin/selenium-server-standalone-2.53.1.jar (see ./scripts/start-selenium.sh)
$ sudo Xvfb :10 -ac
Y finalmente:
Imprescindibles para ejecutar los tests:
Caso de Uso www.takeachef.com
Caso de Uso www.takeachef.com
I follow “Empezar”
Existe otro “EMPEZAR”
más abajo, se actuará
sobre el primero que
encuentre
I follow “EMPEZAR”
Internamente en findAll, dentro de Element.php se
monta el siguiente xpath:
//html/.//a[./@href][((./@id = 'EMPEZAR' or
normalize-space(string(.)) = 'EMPEZAR' or ./@title = 'EMPEZAR'
or ./@rel = 'EMPEZAR') or .//img[./@alt = 'EMPEZAR'])] |
//html/.//*[translate(./@role,'ABCDEFGHIJKLMNOPQRSTUVWX
YZ', 'abcdefghijklmnopqrstuvwxyz') = 'link'][((./@id = 'EMPEZAR'
or ./@value = 'EMPEZAR') or ./@title = 'EMPEZAR' or
normalize-space(string(.)) = 'EMPEZAR')]
Caso de Uso
Caso de Uso
El driver que utilizamos en
Mink es Selenium2
Éste a su vez interactúa
con el driver de Firefox.
Utilizaremos la anotación
@javascript ya que el
asistente sólo funciona con
Javascript
En Selenium Server
Standalone Server 3.* el
driver de Firefox a utilizar es
Gecko
Caso de Uso
I should see “Encuentra tu Chef”
I should see “ENCUENTRA TU CHEF”
El tratamiento interno que hace Mink no
siempre es el mismo. En este caso
MinkContext llama al método de
pagetTextContains el cual busca el texto
mediante una expresión regular
preg_match
Caso de Uso
I click “Continuar”
Debido a un efecto delay en css:
- Hay un hook en BeforeStep,
que espera a que el contenedor
esté del todo visible, utiliza
función spins (alternativa: wait)
- Hay creado 1 Step Definition
propio para que busque el texto
sobre el contenedor que esté
activo y visible
Para interacciones donde la respuesta no es inmediata
Mink ofrece el método wait
$this->getSession()->wait($seconds);
No se considera una buena práctica la espera de
un número determinado de segundos
Pasar una evaluación Javascript es mucho mejor
La comprobación se realiza cada 100 ms hasta un tiempo máximo
expresamente definido
// wait for n milliseconds until JS expression becomes true:
$session->wait(
5000,
"$('.suggestions-results').children().length"
);
Espera a que la expresión javascript
retorne verdadero hasta 5 segundos
public function spins($closure, $seconds = 5, $fraction = 4)
{
$max = $seconds * $fraction;
$i = 1;
while ($i++ <= $max) {
if ($closure($this)) {
return true;
}
$this->getSession()->wait(1000 / $fraction);
}
$backtrace = debug_backtrace();
throw new Exception(
sprintf("Timeout thrown by %s::%s()n%s, line %s" ,
$backtrace[0]['class'], $backtrace[0]['function'],
$backtrace[0]['file'], $backtrace[0]['line']
)
);
}
Timeout 5s por defecto
Fracción de segundo
En casos más complejos o por claridad de código. Función “propia” spins:
Caso de Uso
I fill in the address “Castellón”
En este caso:
- Hay creado 1 Step Definition
propio para gestionar las
direcciones que ofrece la Api de
GoogleMaps.
- También se contempla la función
Spins ya que la carga de datos del
desplegable es asíncrona y
requiere un tiempo indeterminado
Caso de Uso
I click on “2 personas”
I click on “13+ personas desde 35€”
fallará ya que el texto está incluido
en 2 elementos distintos
Caso de Uso
I click on “Quiero dejarme”
I click on “wizard.preferences.surprise”
No hace falta escribir todo el texto
“Quiero dejarme sorprender” aunque
es aconsejable
No usar nunca la key de traducciones
e internamente llamar al servicio
translate o similar del framework
utilizado, puede dar falsos positivos
Caso de Uso
I click on “Enviar”
Finalizamos el proceso
Aparecerá un loader, en este caso
no hace falta utilizar la función
spins ya que no utilizamos ajax,
aunque lo pueda parecer.
Mink se encarga de esperar a que
se cargue la página
correspondiente al enlace clicado.
Caso de Uso
I should see “¡Nuestros chefs ya están
cocinando tu menú!
Creamos un Step Definition propio y
comprobamos que en base de datos se
guardan los datos que hemos introducido en
el test correctamente:
- Solicitud con los datos del test
- Creación de usuario
- Envío de e-mails de confirmación
- Derivación a Chefs (dados de alta
anteriormente mediante DataFixtures)
Práctica discutible, en ocasiones vale la pena ser prácticos
BDD con Mink y Behat
Samuel Vicent Pitarch
samuelvicent@gmail.com
https://www.linkedin.com/in/samuelvicent/
https://twitter.com/samuelvicent
www.geekshubsacademy.com www.takeachef.com

Más contenido relacionado

PDF
[03.1] ciclo de vida del software y ntp 12207
PDF
[05] ciclo de vida del software ntp 12207
PDF
Gestion de proyectos - Estimación del Esfuerzo
DOCX
Documento Vision
PDF
Gerenciamento de problemas e de incidentes
PDF
SEGUNDA PARTE - Gestion de la calidad del software
[03.1] ciclo de vida del software y ntp 12207
[05] ciclo de vida del software ntp 12207
Gestion de proyectos - Estimación del Esfuerzo
Documento Vision
Gerenciamento de problemas e de incidentes
SEGUNDA PARTE - Gestion de la calidad del software

La actualidad más candente (20)

PPT
Requirement Engineering
PPTX
Ingenieria de requerimientos 1
PDF
Sqa ejemplo
DOCX
Unidad 1 requerimientos del software
PDF
Requirements Validation
PDF
Fases de un proyecto de desarrollo de software
DOCX
Software Requirements (3rd Edition) summary
PPTX
MODELO DE PROCESOS DEL SOFTWARE
DOCX
Ensayo CMMI
DOC
Modelos basados en prototipos
PPTX
Software evolution and maintenance basic concepts and preliminaries
PDF
7. cambios en el software y mejora de procesos
PPT
Deontologia del auditor
PDF
Gestion de la configuracion del software
PPTX
Software requirement & specification .pptx
PPTX
calidad de los sistemas de informacion
PPT
Metricas de Codigo Fuente y Metricas de Prueba
PDF
ISO/IEC 12207
PDF
CLASE IX - SISTEMAS BLANDOS
PPT
Software Project Management (lecture 3)
Requirement Engineering
Ingenieria de requerimientos 1
Sqa ejemplo
Unidad 1 requerimientos del software
Requirements Validation
Fases de un proyecto de desarrollo de software
Software Requirements (3rd Edition) summary
MODELO DE PROCESOS DEL SOFTWARE
Ensayo CMMI
Modelos basados en prototipos
Software evolution and maintenance basic concepts and preliminaries
7. cambios en el software y mejora de procesos
Deontologia del auditor
Gestion de la configuracion del software
Software requirement & specification .pptx
calidad de los sistemas de informacion
Metricas de Codigo Fuente y Metricas de Prueba
ISO/IEC 12207
CLASE IX - SISTEMAS BLANDOS
Software Project Management (lecture 3)
Publicidad

Similar a BDD con Mink y Behat (20)

PDF
Automatización de interfaces e introducción a bdd
PDF
Desymfony 2011 - Tutorial #1: Instalacion y primeros pasos
PPTX
Charla Hello Real World para PHPmad
PPTX
DotNetDom: El futuro de Xamarin
PDF
Tech day#7 – especificaciones_ejecutables_y_BDD_con_cucumber_y_selenium
PDF
Tutorial CodeIgniter + Netbeans 7
PDF
Meterpreter en android el desembarco en tu smartphone
PPTX
Webutil
PPTX
Webutil
PPTX
Webutil 090812113116-phpapp01
PPTX
Workshop calabash appium
PDF
¡Que lo haga otro! Automatizaciones SEO para vivir mejor
PPTX
Charla ie
PDF
Desarrollo de aplicaciones multiplataforma 1/2
PDF
Workshop Calabash Appium
PDF
Curso TDD Ruby on Rails #02: Test Driven Development
PDF
Progressive web apps
PDF
Primeros pasos con Backbone js, por Xavier Aznar
PDF
Libro de programación Angular+TypeScripr
PDF
Consumo de APIs usando el WSO2 API Manager
Automatización de interfaces e introducción a bdd
Desymfony 2011 - Tutorial #1: Instalacion y primeros pasos
Charla Hello Real World para PHPmad
DotNetDom: El futuro de Xamarin
Tech day#7 – especificaciones_ejecutables_y_BDD_con_cucumber_y_selenium
Tutorial CodeIgniter + Netbeans 7
Meterpreter en android el desembarco en tu smartphone
Webutil
Webutil
Webutil 090812113116-phpapp01
Workshop calabash appium
¡Que lo haga otro! Automatizaciones SEO para vivir mejor
Charla ie
Desarrollo de aplicaciones multiplataforma 1/2
Workshop Calabash Appium
Curso TDD Ruby on Rails #02: Test Driven Development
Progressive web apps
Primeros pasos con Backbone js, por Xavier Aznar
Libro de programación Angular+TypeScripr
Consumo de APIs usando el WSO2 API Manager
Publicidad

Último (20)

PDF
Estructura del Plan Estratégico Institucional del Ministerio Público de Perú_...
PDF
CIENCIAS SOCIALES HISTORIA identificamos las características de la independen...
PPTX
Teología 1 - Unidad 1. Introducción.pptx
PPTX
Psicologia politica: Antecedentes e Hisotoria.
PPTX
Casa de Boyacá informe de actividades 2024
PDF
7.3 Audiencias de Reforma y Revisión en el Proceso.pdf
PDF
Las finanzas Bíblicas, dando un mejor resultado
PPTX
NORMA 029 STPS INSTALACIONES ELECTRICAS.
PDF
HISTORIA DE LA ARQUITECTURA ANALIS DE CATEDRAL
PPTX
PRESENTACIONES CHAPA PARA TESIS SECUNDARIA.pptx
PPTX
Sectas Protestantes y la Iglesia que fundó Cristo
PDF
evaluacion de riesgos conceptos y herram
PDF
La castidad nos hace libres para amar (Jovenes).pdf
PPTX
Difusión Empresa Protocolo TMERT V2.pptx
PPTX
Alta presión en productos de la carne de cerdo
PDF
2. Análisis Visión-Misión-Valores,OEI y PEND Ministerio Público.pdf
PPTX
OIDO histoembriologi, resumen corto de la clase
PPTX
Yo seré tu kokua la compañera que anhelas .pptx
PPTX
Heridas en traumatología en estudio .pptx
PPTX
EJEMPLO DE PRESENTACIÓN TESIS PARA EXPONER.pptx
Estructura del Plan Estratégico Institucional del Ministerio Público de Perú_...
CIENCIAS SOCIALES HISTORIA identificamos las características de la independen...
Teología 1 - Unidad 1. Introducción.pptx
Psicologia politica: Antecedentes e Hisotoria.
Casa de Boyacá informe de actividades 2024
7.3 Audiencias de Reforma y Revisión en el Proceso.pdf
Las finanzas Bíblicas, dando un mejor resultado
NORMA 029 STPS INSTALACIONES ELECTRICAS.
HISTORIA DE LA ARQUITECTURA ANALIS DE CATEDRAL
PRESENTACIONES CHAPA PARA TESIS SECUNDARIA.pptx
Sectas Protestantes y la Iglesia que fundó Cristo
evaluacion de riesgos conceptos y herram
La castidad nos hace libres para amar (Jovenes).pdf
Difusión Empresa Protocolo TMERT V2.pptx
Alta presión en productos de la carne de cerdo
2. Análisis Visión-Misión-Valores,OEI y PEND Ministerio Público.pdf
OIDO histoembriologi, resumen corto de la clase
Yo seré tu kokua la compañera que anhelas .pptx
Heridas en traumatología en estudio .pptx
EJEMPLO DE PRESENTACIÓN TESIS PARA EXPONER.pptx

BDD con Mink y Behat

  • 1. Mink & Behat BDD - Behaviour Driven Development
  • 3. Es un proceso de desarrollo de software Un tipo de TDD... pero más verboso donde intervienen varios colaboradores (stakeholders) ¿Qué es BDD?
  • 5. Tipos de BDD Story BDD* se lleva a cabo con Behat Enfocado en la narrativa = QUÉ
  • 6. Tipos de BDD Spec BDD se lleva a cabo con PHPSpec Enfocado en la implementación = CÓMO
  • 8. Proyecto Open Source creado por Konstantin Kudryashov Desarrollo albergado en GitHub: https://github.com/Behat/Behat ¿Qué es Behat?
  • 9. ¿Qué es Behat? Un framework para testing en BDD (Behaviour Driven Development) Escrito en php y para php 5.3+
  • 10. Inspirado en Cucumber, de Ruby (https://cucumber.io/) Behat 3 está considerado como la versión oficial de la implementación de Cucumber para php y muy bien considerado en el mundo del BDD ¿Qué es Behat?
  • 11. Behat usa como lenguaje para contar historias: Gherkin Gherkin es un DSL, Domain Specific Language, como SQL ¿Qué es Behat?
  • 12. Gherkin usa un lenguaje humano real estructurado Uno modo de escribir una historia que puede ser automatizada ¿Qué es Behat?
  • 13. Las sentencias se pueden escribir en varios idiomas NO es un lenguaje de programación ¿Qué es Behat?
  • 14. Promueve la comunicación entre todas las partes que intervienen en la empresa, como por ejemplo: la parte de negocio y la parte de desarrollo ¿Por qué Behat? Ubiquitous Language (Martin Fowler): https://martinfowler.com/bliki/UbiquitousLanguage.html UBIQUITOUS LANGUAGE
  • 15. Feature: The Coffee Machine serves coffees In order to take a coffee As a buyer I should be able to buy a coffee to stay awake Scenario: Buy one coffee Given Coffee Machine can serve up to 10 coffees at $0.60 When I deposit 1 dollar And I press the coffee button Then I should be served a coffee And I should be returned $0.40 And There should be 9 coffees left Vamos a contar una historia
  • 16. Feature: The Coffee Machine serves coffees In order to take a coffee As a buyer I should be able to buy a coffee to stay awake Scenario: Buy one coffee Given Coffee Machine can serve up to 10 coffees at $0.60 When I deposit 1 dollar And I press the coffee button Then I should be served a coffee And I should be returned $0.40 And There should be 9 coffees left QUÉ POR QUÉ QUIEN rol de usuario
  • 17. Feature: The Coffee Machine serves coffees In order to take a coffee As a buyer I should be able to buy a coffee to stay awake Scenario: Buy one coffee Given Coffee Machine can serve up to 10 coffees at $0.60 When I deposit 1 dollar And I press the coffee button Then I should be served a coffee And I should be returned $0.40 And There should be 9 coffees left 1 única Feature (Historia) por archivo .feature N Scenarios por Feature
  • 18. Feature: The Coffee Machine serves coffees In order to take a coffee As a buyer I should be able to buy a coffee to stay awake Scenario: Buy one coffee Given Coffee Machine can serve up to 10 coffees at $0.60 When I deposit 1 dollar And I press the coffee button Then I should be served a coffee And I should be returned $0.40 And There should be 9 coffees left Sentences: Acciones/Steps a testear
  • 19. Feature: The Coffee Machine serves coffees In order to take a coffee As a buyer I should be able to buy a coffee to stay awake Scenario: Buy one coffee Given Coffee Machine can serve up to 10 coffees at $0.60 When I deposit 1 dollar And I press the coffee button Then I should be served a coffee And I should be returned $0.40 And There should be 9 coffees left precondiciones/ inicializaciones interacción de usuario resultados
  • 20. ¿Dónde ocurre la magia? ¿Y dónde está la implementación de los tests?
  • 21. Behat “mapea” definiciones Gherkin con implementación Php y las relaciona mediante anotaciones Scenario Steps .feature acciones en Gherkin Step Definitions FeatureContext.php Código PHP
  • 22. /** * @When I deposit 1 dollar */ public function iDepositOneDollar() { // do something } Feature: The Coffee Machine serves coffees ... Scenario: Buy one coffee Given ... When I deposit 1 dollar And ... gherkin .feature FeatureContext.php anotación + método
  • 23. 1.- Instalar composer 2.- Crear archivo composer.json 3.- Ejecutar composer install { "require-dev": { "behat/behat": "3.3.1" }, "config": { "bin-dir": "bin/" } } Configuración proyecto básico Behat
  • 24. 4.- Inicializar Behat para configurar un framework base: ./bin/behat --init 5.- Tras crear los tests ejecutar ./bin/behat 6.- Opcionalmente crear archivo behat.yml (configuración avanzada) bin composer.json composer.lock features |_ bootstrap |_ FeatureContext.php vendor ... Ubicación para los archivos .feature Ubicación archivos *Context.php Configuración proyecto básico Behat
  • 25. Una puede definir uno o más Scenario Cada testea un comportamiento Cada puede definir uno o más Steps Los Steps se pueden encadenar Los Comentarios están permitidos Given When And/But Then Scenario Scenario Feature # this is a comment Qué define cada Keyword
  • 26. Scenario Outline Background Given ( + table transformation ) Examples ( + table ) Más definiciones de Keywords Se ejecuta el mismo Scenario para cada fila de la tabla Todos los Steps dentro de Background se iniciarán antes de cada Scenario Nos permite inicializar datos para trabajar dentro de un Scenario
  • 27. Escritura de una Feature: GOOD PRACTICE Explica QUÉ es lo que se desea hacer y NO CÓMO de modo que todo el mundo pueda entender la situación Pensar 2 veces antes de escribir una frase de modo que cualquiera pueda entender el caso * * caso usb
  • 28. Indicar expresamente el valor de un elemento de formulario Utilizar en una sentencia el valor de un id Bad: I press the button with id “server-coffee” Escritura de una Feature: BAD PRACTICE
  • 29. Good: I press the coffee button Escritura de una Feature: GOOD PRACTICE Indicar cuál es la acción que se desea llevar a cabo, no cómo
  • 31. ¿Qué es Mink? Mink es un proyecto Open Source para PHP5.3+ que permite controlar/simular un navegador web y poder así testear aplicaciones web
  • 32. El desarrollo está hospedado en GitHub: https://github.com/minkphp/Mink Desarrollado y mantenido por varios contribuidores ¿Qué es Mink?
  • 33. Con Mink es posible Controlar el Navegador Recorrer un página Manipular elementos de un página Interaccionar con una página
  • 34. Controlar el Navegador Ej: $this->getSession()->visit(‘http://web-site.ext’); ¿Qué permite llevar a cabo Mink?
  • 35. Recorrer una página Ej: $this->getSession()->getPage()->find(‘css’, ‘h1’); ¿Qué permite llevar a cabo Mink?
  • 36. Manipular elementos de una página Ej: $this->getSession()->getPage()->getText(); ¿Qué permite llevar a cabo Mink?
  • 37. Interaccionar con una página Ej: $this->getSession()->getPage()->findById(‘div-cta’)->click(); ¿Qué permite llevar a cabo Mink?
  • 38. ¿Qué es Mink? Mink provee principalmente 4 objetos DRIVER SESSION NODE ELEMENTPAGE
  • 39. DRIVER: $goutteDriver = new GoutteDriver(); SESSION: $this->getSession() PAGE: $this->getSession()->getPage() NODEELEMENT: $this->getSession()->getPage()->find(‘h1’)
  • 40. ¿Por qué Mink? Mink implementa drivers específicos para cada Emulador Web abstrayendo llamadas API específicas Goutte Selenium2 Zombie
  • 41. ¿Por qué Mink? Puede “lanzar navegadores” Clientes Web con Interfaz gráfica Servidores web sin Interfaz Gráfica
  • 42. Permite testear distintas resoluciones web ¿Por qué Mink?
  • 43. Permite testear distintos motores de navegadores web ¿Por qué Mink?
  • 44. En sistemas operativos diversos ¿Por qué Mink?
  • 45. Se integra bien con “Navegadores” Web gracias a: Selenium & Sahi Sahi package abandonado y sin mantener: https://packagist.org/packages/behat/mink-sahi-driver ¿Por qué Mink?
  • 46. Es posible testear funcionalidades javascript ¿Por qué Mink?
  • 47. Se puede integrar fácilmente con frameworks para Php tales como Symfony/Laravel ¿Por qué Mink?
  • 48. Ofrece un API común para todos los diferentes Emuladores Web mediante la definición de una interfaz (contrato) ¿Por qué Mink?
  • 49. 2 Tipos de Emuladores Web 1.- Emuladores Web Headless (sin cabeza) 2.- Controladores Web (Browser Controllers)
  • 50. Son simplemente implementaciones de especificaciones HTTP Emuladores Web Headless No se puede testear JS Rápidos (Goutte) cUrl o Guzzle
  • 51. Su objetivo es controlar un navegador web real Controladores Web (Browser Controllers) SI que puede testear JS! Lento (Selenium/Sahi)
  • 52. ¿Cómo funciona Mink con Selenium? Selenium2 Chrome Gecko En otra terminal se inicia una instancia de Selenium Server Standalone (.jar) el cual Interacciona con los motores de navegadores reales directamente o vía drivers específicosMink inicia una sesión con un driver específico Este driver a su vez se comunica con la aplicación/driver específica del navegador web 1 2 3
  • 54. Depende del tipo de test Entonces … ¿cuál es mejor?
  • 55. Mink & Behat El binomio y
  • 56. ¿Por qué Behat y Mink? La ecuación permite testear el comportamiento de un proceso web completo
  • 57. El binomio actúa como intermediario Product Managers y clientes pueden contar una historia (mediante un lenguaje estructurado) De modo que los desarrolladores pueden entender fácilmente los requerimientos e implementarlos ¿Por qué Behat y Mink?
  • 58. Es un modo natural de validar los requerimientos ¿Por qué Behat y Mink? Promueve escribir los tests antes de escribir una línea de código
  • 59. código de mejor calidad ¿Por qué Behat y Mink?
  • 60. ayuda a evitar malentendidos ¿Por qué Behat y Mink? UBIQUITOUS LANGUAGE
  • 61. Navegando por Wikipedia Mink & Behat Ejemplo práctico
  • 62. Navegando por Wikipedia Visitar la página de Wikipedia en Inglés Buscar la definición de Behat Guardar las referencias en un archivo de texto ¿Qué se pretende
  • 63. Dependencias (composer.json) { "require-dev" : { "behat/behat": "3.1.1", "behat/mink": "^1.6", "behat/mink-extension" : "^2.2", "behat/mink-goutte-driver" : "^1.2" }, "config": { "bin-dir": "bin/" } } Ejemplo práctico - Scrapper Biblioteca Mink Integración entre Behat y Mink * (incluye MinkContext) Driver de Mink para Goutte (package independiente = buena práctica) Biblioteca Behat
  • 64. Inicialización del framework Behat: ./bin/behat --init bin composer.json composer.lock features |_ bootstrap |_ FeatureContext.php vendor ... Ubicación para los archivos .feature Ubicación archivos *Context.php Ejemplo práctico - Scrapper
  • 65. default: suites: default: contexts: - FeatureContext - BehatMinkExtensionContextMinkContext extensions: BehatMinkExtension : base_url: http://en.wikipedia.org goutte: ~ Archivo de configuración behat.yml MinkContext ofrece una colección de Step Definitions gratis! Mink Extension (pegamento entre Behat y Mink) Ejemplo práctico - Scrapper
  • 66. default | When /^(?:|I )go to (?:|the )homepage$/ default | Then /^(?:|I )should be on "(?P<page>[^"]+)"$/ default | When /^(?:|I )follow "(?P<link>(?:[^"]|")*)"$/ default | When /^(?:|I )press "(?P<button>(?:[^"]|")*)"$/ default | Then /^(?:|I )should see "(?P<text>(?:[^"]|")*)"$/ default | Then /^(?:|I )should not see "(?P<text>(?:[^"]|")*)"$/ default | When /^(?:|I )fill in "(?P<field>(?:[^"]|")*)" with "(?P<value>(?:[^"]|")*)"$/ default | Then /^print current URL$/ default | Then /^print last response$/ default | When /^(?:|I )select "(?P<option>(?:[^"]|")*)" from "(?P<select>(?:[^"]|")*)"$/ default | When /^(?:|I )check "(?P<option>(?:[^"]|")*)"$/ default | Then /^the "(?P<checkbox>(?:[^"]|")*)" checkbox should be checked$/ default | Then /^(?:|I )should see "(?P<text>(?:[^"]|")*)" in the "(?P<element>[^"]*)" element$/ Algunas Step Definitions que nos ofrece gratis Mink vía MinkContext: $ ./bin/behat -dl Nota: Otros contextos interesantes: Behatch, StepThroughExtension, PageObjectExtension
  • 67. Visit "https://www.wikipedia.org" and click on "English" Navegando por Wikipedia I am redirected to "wiki/Main_Page"
  • 68. I search "behat computer" and click on "Behat (computer science)" Navegando por Wikipedia
  • 69. Save references into a text file Navegando por Wikipedia
  • 70. Feature: Scrap behat references from wikipedia In order get behat references As an anonymous user I visit wikipedia behat entry and scrap the references list @javascript Scenario: Scrap references Given I am on "https://www.wikipedia.org" #Given I go to homepage Then I should see "The Free Encyclopedia" When I follow "English" Then the url should match "wiki/Main_Page" And I should see "Welcome to Wikipedia," When I fill in "Search Wikipedia" with "behat computer" And I press "Search Wikipedia" And print current URL #Debug And I follow "Behat (computer science)" Then I should see "Behat is intended to aid communication between" And I save references in a local storage device#Sentencia a implementar Archivo .feature: features/wikipedia.scrapper.feature
  • 71. use BehatMinkExtensionContextRawMinkContext; class FeatureContext extends RawMinkContext implements Context, SnippetAcceptingContext /** * @Given /^I save references in a local storage device$/ */ public function iSaveReferencesInALocalStorageDevice () { … } FeatureContext.php Requerido para obtener una Session de Mink
  • 72. /** @Given /^I save references in a local storage device$/ */ public function iSaveReferencesInALocalStorageDevice () { ... } Step Definition propio Método que guarda las preferencias. Paso a paso
  • 73. /** @Given /^I save references in a local storage device$/ */ public function iSaveReferencesInALocalStorageDevice () { $page = $this->getSession()->getPage(); ... } Obtener el objeto Page Obtener el objeto Session (pensar en una pestaña del navegador) Método que guarda las preferencias. Paso a paso
  • 74. /** @Given /^I save references in a local storage device$/ */ public function iSaveReferencesInALocalStorageDevice () { $page = $this->getSession()->getPage(); $content = $page->find('named', array('id', 'mw-content-text' )); } named Selector buscar por id Método que guarda las preferencias. Paso a paso
  • 75. /** @Given /^I save references in a local storage device$/ */ public function iSaveReferencesInALocalStorageDevice () { ... $references = $content->find('css', '.references'); $items = $references->findAll('css', 'li'); } Selectores CSS ¡Atención con el punto! find vs findAll Método que guarda las preferencias. Paso a paso
  • 76. /** @Given /^I save references in a local storage device$/ */ public function iSaveReferencesInALocalStorageDevice () { ... $links = array(); foreach ($items as $item) { $linkContainer = $item->find('xpath', '//span[@class="reference-text"]' ); $links[] = $linkContainer ->find('xpath', '//a/@href')->getText(); } } Selectores xpath obtener sólo el valor del enlace Método que guarda las preferencias. Paso a paso
  • 77. /** @Given /^I save references in a local storage device$/ */ public function iSaveReferencesInALocalStorageDevice () { ... file_put_contents ('scrapped_references.txt' , join(PHP_EOL, $links)); } Guardar el archivo en disco Método que guarda las preferencias. Paso a paso
  • 78. /** @Given /^I save references in a local storage device$/ */ public function iSaveReferencesInALocalStorageDevice () { $page = $this->getSession()->getPage(); $content = $page->find('named', array('id', 'mw-content-text' )); $references = $content->find('css', '.references'); $items = $references->findAll('css', 'li'); $links = array(); foreach ($items as $item) { $linkContainer = $item->find('xpath', '//span[@class="reference-text"]' ); $links[] = $linkContainer ->find('xpath', '//a/@href')->getText(); } file_put_contents ('scrapped_references.txt' , join(PHP_EOL, $links)); } 2.- Css Selectors 3.- Xpath Selectors 1.- Named Selector Método que guarda las preferencias. Paso a paso
  • 79. : implementar la misma solución con menor código Vamos a Refactorizar
  • 80. /** @Given /^I save references in a local storage device again$/ */ public function iSaveReferencesInALocalStorageDeviceAgain () { $closure = function($item) { return $item->getText(); }; // anonymous function $xpath = '//span[@class="reference-text"]/a/@href'; $links = array_map($closure, $this->findAll('xpath', $xpath)); file_put_contents ('scrapped_references_again.txt' , join(PHP_EOL, $links)); } public function __call($method, $parameters) { $page = $this->getSession()->getPage(); if (method_exists($page, $method)) { return call_user_func_array (array($page, $method), $parameters); } } Magic Call Sólo un selector xpath Método que guarda las preferencias. Solución corta Closure vs Lambda: https://www.ibm.com/developerworks/library/os-php-5.3new2/os-php-5.3new2-pdf.pdf
  • 82. bdd# ./bin/behat Feature: Scrap behat references from wikipedia ... Scenario: Scrap References # features/wikipedia.scrapper.feature:6 Given I am on " https://www.wikipedia.org" # BehatMinkExtensionContextMinkContext::visit() Then I should see " The Free Encyclopedia" # Behat...MinkContext::assertPageContainsText() When I follow " English" # Behat...MinkContext::clickLink() Then the url should match "wiki/Main_Page" # Behat...MinkContext::assertUrlRegExp() And I should see " Welcome to Wikipedia," # Behat...MinkContext::assertPageContainsText() When I fill in " Search Wikipedia" with "behat computer" # Behat...MinkContext::fillField() And I press " Search Wikipedia" # Behat...MinkContext::pressButton() And I follow " Behat (computer science)" # Behat...MinkContext::clickLink() Then I should see " Behat is intended to aid communication between developers, clients and other stakeholders during a..." # Behat...MinkContext::assertPageContainsText() And I save references in a local storage device # FeatureContext::iSaveReferencesInALocalStorageDevice() 1 scenario (1 passed) 10 steps (10 passed) 0m4.15s (15.36Mb)
  • 84. Echando una mano a Mink El flujo de ejecución de Behat está basado en eventos Behat provee 8 Hooks de gran ayuda. Principio DRY Los eventos se “mapean” mediante anotaciones Las anotaciones simplifican la gestión de Datafixtures Behat ofrece tags predefinidos (@javascript) y admite tags personalizados
  • 86. 1) Java JRE 2) Firefox, Chrome y/o otros drivers. Ver sección Third Party Drivers, Bindings, and Plugins at http://www.seleniumhq.org/download) 3) Selenium Standalone Server (http://www.seleniumhq.org/) 4) xvfb (X Virtual Frame Buffer). Permite crear una “gráfica virtual” en memoria. 5) Servidor web integrado por PHP (bin/console server:run) o un Servidor web con urlRewrite (Apache/nginx) y host debidamente configurado. Imprescindibles para ejecutar los tests: Nota: Firefox v.46 con selenium-server-standalone-2.53.1.jar funciona sin problemas
  • 87. En resumen: $ ./bin/behat --config ./app/config/behat.yml $ ./bin/console server:start $ java -jar ./bin/selenium-server-standalone-2.53.1.jar (see ./scripts/start-selenium.sh) $ sudo Xvfb :10 -ac Y finalmente: Imprescindibles para ejecutar los tests:
  • 88. Caso de Uso www.takeachef.com
  • 89. Caso de Uso www.takeachef.com I follow “Empezar” Existe otro “EMPEZAR” más abajo, se actuará sobre el primero que encuentre
  • 90. I follow “EMPEZAR” Internamente en findAll, dentro de Element.php se monta el siguiente xpath: //html/.//a[./@href][((./@id = 'EMPEZAR' or normalize-space(string(.)) = 'EMPEZAR' or ./@title = 'EMPEZAR' or ./@rel = 'EMPEZAR') or .//img[./@alt = 'EMPEZAR'])] | //html/.//*[translate(./@role,'ABCDEFGHIJKLMNOPQRSTUVWX YZ', 'abcdefghijklmnopqrstuvwxyz') = 'link'][((./@id = 'EMPEZAR' or ./@value = 'EMPEZAR') or ./@title = 'EMPEZAR' or normalize-space(string(.)) = 'EMPEZAR')] Caso de Uso
  • 91. Caso de Uso El driver que utilizamos en Mink es Selenium2 Éste a su vez interactúa con el driver de Firefox. Utilizaremos la anotación @javascript ya que el asistente sólo funciona con Javascript En Selenium Server Standalone Server 3.* el driver de Firefox a utilizar es Gecko
  • 92. Caso de Uso I should see “Encuentra tu Chef” I should see “ENCUENTRA TU CHEF” El tratamiento interno que hace Mink no siempre es el mismo. En este caso MinkContext llama al método de pagetTextContains el cual busca el texto mediante una expresión regular preg_match
  • 93. Caso de Uso I click “Continuar” Debido a un efecto delay en css: - Hay un hook en BeforeStep, que espera a que el contenedor esté del todo visible, utiliza función spins (alternativa: wait) - Hay creado 1 Step Definition propio para que busque el texto sobre el contenedor que esté activo y visible
  • 94. Para interacciones donde la respuesta no es inmediata Mink ofrece el método wait $this->getSession()->wait($seconds); No se considera una buena práctica la espera de un número determinado de segundos
  • 95. Pasar una evaluación Javascript es mucho mejor La comprobación se realiza cada 100 ms hasta un tiempo máximo expresamente definido // wait for n milliseconds until JS expression becomes true: $session->wait( 5000, "$('.suggestions-results').children().length" ); Espera a que la expresión javascript retorne verdadero hasta 5 segundos
  • 96. public function spins($closure, $seconds = 5, $fraction = 4) { $max = $seconds * $fraction; $i = 1; while ($i++ <= $max) { if ($closure($this)) { return true; } $this->getSession()->wait(1000 / $fraction); } $backtrace = debug_backtrace(); throw new Exception( sprintf("Timeout thrown by %s::%s()n%s, line %s" , $backtrace[0]['class'], $backtrace[0]['function'], $backtrace[0]['file'], $backtrace[0]['line'] ) ); } Timeout 5s por defecto Fracción de segundo En casos más complejos o por claridad de código. Función “propia” spins:
  • 97. Caso de Uso I fill in the address “Castellón” En este caso: - Hay creado 1 Step Definition propio para gestionar las direcciones que ofrece la Api de GoogleMaps. - También se contempla la función Spins ya que la carga de datos del desplegable es asíncrona y requiere un tiempo indeterminado
  • 98. Caso de Uso I click on “2 personas” I click on “13+ personas desde 35€” fallará ya que el texto está incluido en 2 elementos distintos
  • 99. Caso de Uso I click on “Quiero dejarme” I click on “wizard.preferences.surprise” No hace falta escribir todo el texto “Quiero dejarme sorprender” aunque es aconsejable No usar nunca la key de traducciones e internamente llamar al servicio translate o similar del framework utilizado, puede dar falsos positivos
  • 100. Caso de Uso I click on “Enviar” Finalizamos el proceso Aparecerá un loader, en este caso no hace falta utilizar la función spins ya que no utilizamos ajax, aunque lo pueda parecer. Mink se encarga de esperar a que se cargue la página correspondiente al enlace clicado.
  • 101. Caso de Uso I should see “¡Nuestros chefs ya están cocinando tu menú! Creamos un Step Definition propio y comprobamos que en base de datos se guardan los datos que hemos introducido en el test correctamente: - Solicitud con los datos del test - Creación de usuario - Envío de e-mails de confirmación - Derivación a Chefs (dados de alta anteriormente mediante DataFixtures) Práctica discutible, en ocasiones vale la pena ser prácticos