Luego de estar unos meses poniendo en practica TDD se dio la oportunidad de comenzar un nuevo proyecto y poder hacerlo mediante BDD. Así que decidí ensuciarme un poco las manos y ponerlo a prueba. El enfoque usado seria el mismo, primero definimos que es lo que hay que implementar para luego ponernos a solucionar el problema. La diferencia es la forma en que se define cada uno de ellos, TDD esta centrado para el equipo de desarrollo mientras que BDD esta pensado tanto como los desarrolladores como para la gente no entiende absolutamente nada de programación (al menos una parte). Defiendo los requisitos mediante pruebas unitarias (TDD) y historias de usuario (BDD).

Pongamos por ejemplo una calculadora, para definir la capacidad de realizar una suma en TDD seria algo asi:

1
2
3
4
5
public void testSuma() {
assertEqualas(4, Calculadora.suma(2, 2));
assertEqualas(0, Calculadora.suma(2, -2));
assertEqualas(-4, Calculadora.suma(-2, -2));
}

Mientras que en BDD es mucho mas legible para la gente no-técnica.

1
Given I have a calculator
When I add 2 and 2
Then the result should be 4
 
Given I have a calculator
When I add 2 and -2
Then the result should be 0
 
Given I have a calculator
When I add -2 and -2
Then the result should be -4

¿No hace falta comentar la diferencia no?

Obviamente con definir la historia de usuario no alcanza, el siguiente paso es asociar mediante una expresión regular cada linea de la historia de usuario a una función. Suena horrorosamente complicado, pero es mucho mas simple de lo que parece. En este caso en concreto veremos como hacerlo con Behat y Mink.

Behat

Behat como dice en su web se un framework php para probar las expectativas de negocio. Es quien se encarga de la ejecución de las pruebas historias de usuario. Aparte de lo que es fichero de configuración de Behat hay dos componentes claves en su funcionamiento.

El primer componente serian las historias de usuario, estas se definen en ficheros .feature mediante el lenguaje Gherkin (el snippet anterior esta hecho en Gherkin) cada funcionalidad a implementar se define en un fichero dividido en tres partes:

  1. Feature: descripción de la característica a implementar.
  2. Background: definición del contexto en el cual vamos a ejecutar las pruebas (un fixture, vamos).
  3. Scenarios: los escenarios definen las partes necesarias para implementar la nueva característica, a su vez cada escenario esta compuesto por un numero finito de steps (pasos).

Mink

Mink es la otra pieza clave para poder llevar a cabo BDD en un entorno web. Nos brindara un navegador, ya sea en php puro como Goutte (el usado por Symfony 2), este tipo de navegadores suelen ser muy rápidos pero no podremos interactuar ni ver los resultados obtenidos al aplicar javascript en la parte del cliente; también podemos usar un navegador real a través de Sahi donde veremos los resultados que se producen al ejecutar javascript en nuestra pagina. Se puede usar otro dos navegadores Zombie y Selenium, la configuración de cada uno de ellos esta detallada en la documentación

Ademas de encapsular el navegador nos brinda una serie de funciones predefinidas para poder interactuar con él. En esta cheatsheet pueden consultar las funciones definidas.

Symfony 2

La integración de Behat y Mink en nuestro proyecto Symfony 2 es bastante sencilla, como ya es costumbre basta con agregar las dependencias a composer. Dependiendo si usamos Mink u otro de los drivers proporcionados tendremos que agrega la correspondiente dependencia.

Una vez instalados tendremos que crear un fichero de configuración en la raiz de nuestro proyecto llamado behat.yml y generar la estructura necesaria para la ejecución de las pruebas. El ultimo paso se hace llamando a:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
	vendor/bin/behat --init
```
Creando la carpeta Features dentro de nuestro bundle.

``` yaml
default:
paths:
features: src/Acme/MyBundle/Features
bootstrap: %behat.paths.features%/bootstrap
extensions:
Behat\Symfony2Extension\Extension:
mink_driver: true
kernel:
env: test
debug: true
bundle: AcmeMyBundle

Behat\MinkExtension\Extension:
default_session: symfony2

Al ejecutar las pruebas no saldrán dos errores, diciendo que no esta definido los siguientes pasos:

  1. Given There is no “User” in database
  2. And the following users

Si lo volvemos a ejecutar con la opción —append-snippets, veremos que en el fichero FeatureContext tendremos agregadas las dos funciones a ejecutar cuando se ejecuta cada uno de esos pasos, faltandonos solo definir el cuerpo de cada función según nuestras necesidades

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
class FeatureContext extends MinkContext
implements KernelAwareInterface
{

private $kernel;
private $parameters;

/**
* Initializes context with parameters from behat.yml.
*
* @param array $parameters
*/

public function __construct(array $parameters)
{

$this->parameters = $parameters;
}

/**
* Sets HttpKernel instance.
* This method will be automatically called by Symfony2Extension ContextInitializer.
*
* @param KernelInterface $kernel
*/

public function setKernel(KernelInterface $kernel)
{

$this->kernel = $kernel;
}

/**
* @Given /^There is no "([^"]*)" in database$/
*/

public function thereIsNoInDatabase($arg1)
{

//delete table
}

/**
* @Given /^the following users:$/
*/

public function theFollowingUsers(TableNode $table)
{

//iterate table and create users
}
}

Y eso seria todo cuanto necesitamos para empezar a aplicar BDD en un proyecto Symfony2.