BDD con Behat – Introducción

Introducción

Este es un post de carácter introductorio que ha surgido con la idea de que les sirva a aquellos desarrolladores que anden algo perdidos, y quieran un punto de inicio que les sirva de empuje para adentrarse en la creación de tests para sus aplicaciones. Vamos a tratar de raspar un poco la superficie de BDD con Behat.

Debido al enfoque del post, es posible que accedas aquí sin saber muy bien por qué es necesario tener el código de tu aplicación acompañado de tests. Un test es una serie de instrucciones que comprueban el buen funcionamiento del código que desarrollas, facilitando el encuentro de bugs y errores que podamos introducir en modificaciones posteriores, o comportamientos no deseados de una funcionalidad que desarrollemos. Como empezarás a pensar, su misión es que descanses tranquil@ tras ese merge a master y subida a producción.

Pero antes de nada, es importante que no te agobies, no pienses que todo el código tiene que tener cobertura de tests, pues es algo costoso, céntrate en la lógica esencial e importante del negocio de tu aplicación, hay partes de una aplicación, que aunque no sea deseable, pueden dejar de funcionar tras una subida a producción, o por un pico de usuarios, pero otras, como por ejemplo, los sistemas de pagos, han de ser fiables siempre, es ahí donde has de enfocar tus energías.

Por otro lado, la escritura de test tiene también su aprendizaje, pues los tests deben de ser útiles y válidos, es decir, no sirve de nada pasar varios tests que no cumplen la lógica correcta que busca la funcionalidad que has de desarrollar, por lo que empieza con tests sencillos y complícalos según vayas desarrollando tus habilidades.

Ya que vamos a ver BDD con Behat, es necesario primero hablar un poco sobre BDD.

BDD

¿Cómo empezamos? Es necesario conocer TDD (Test Driven Development) y BDD (Behaviour Driven Development). El primero se centra en realizar tests, escribir código, y refinar cuando falle para perfeccionar nuestro código. Con el segundo entran en juego el comportamiento del código que validamos, y es donde vamos a centrarnos en este post.

En BDD, validamos que nuestro código cumpla una “historia de usuario”. Una “historia de usuario” deberá reunir al menos la información que defina la funcionalidad, y aunque las puedas describir de diferentes formas, suele plantearse de la siguiente manera:

User story title: [una breve descripción de la historia]

As a [rol], 
I want [funcionalidad]
so that I can [beneficio]. 

Acceptance Criterion: [los escenarios que comprobaremos en nuestra funcionalidad]

Scenario n: [título del escenario]
Given [contexto]
   And [algo más de contexto],
When [evento]
Then [resultado]
   And [otro resultado].

Si queréis profundizar más en la escritura de “historias de usuario”, podéis pasaros por esta entrada que ahonda bastante más en ello.

Volviendo a los tests, las herramientas BDD nos permiten adaptar estas historias a features con escenarios en lenguaje DSL (domain specific language) que podremos usar en la creación de nuestros tests. Veréis más adelante cómo de fácil es traducir la plantilla anterior a un test.

¿Qué es Behat?

Para poder usar BDD en nuestros proyectos, y beneficiarnos de sus ventajas, usaremos Behat, que es la herramienta que nos permitirá realizar las pruebas sobre nuestro código. Behat nació con la idea de trasladar Cucumber al lenguaje PHP y, al igual que este, hace uso del lenguaje Gherkin para la escritura de los tests, así como de los ficheros con extensión “.feature”.

Vale, vamos a bajar estos conceptos para empezar a unir los puntos.

Veíamos antes que con BDD describimos el comportamiento que ha de realizar las funcionalidades de nuestra aplicación. Behat nos permite describir estos comportamientos con el lenguaje DSL Gherkin y dividirlos en distintos ficheros según la funcionalidad a comprobar. Lo mejor es ver un fichero de ejemplo donde podáis ver la estructura, comprobaréis que al comienzo, definimos la funcionalidad, y cada uno de los criterios de aceptación vienen contemplados en los escenarios:

Feature: Create a new shop
  In order to create a new shop
  As an admin
  I need to be capable of add new shop via create form.

Scenario: Create a new valid shop
  Given I am on "/shop"
  When I follow "Create a new shop"
    And I fill in "Name" with "Test"
    And I fill in "Address" with "Fake road"
    And I fill in "Category" with "Tests"
    And I press "Create"
  Then I should see "Shop"
    And I should see "Test"
    And I should see "Edit"

Instalación

La instalación de Behat en nuestro proyecto es muy sencilla si la realizamos a través de composer. Antes de nada, comprobamos que tenemos definido el directorio “bin” en nuestro composer.json:

# composer.json
[...]

{
    "config": {
        "bin-dir": "bin/"
    }
}

[...]

Después, instalamos los siguientes paquetes a través de composer:

$ composer require –dev behat/behat behat/mink-extension behat/mink-goutte-driver

Acabamos de instalar en nuestro proyecto Behat y Mink, pero hemos de pararnos un momento para explicar qué es Mink. Para que Behat sea capaz de realizar las comprobaciones y navegaciones de nuestros tests, es necesario usar un servicio que emula por detrás un navegador para poder, bajo nuestras instrucciones, visitar los enlaces, rellenar campos de formularios, etc.

Pero Mink no viaja sólo, requiere de una capa adicional, un driver de entre la siguiente lista que afectará en las posibilidades y potencia de las pruebas que podamos realizar:

  • GoutteDriver – behat/mink-goutte-driver
  • Selenium2Driver – behat/mink-selenium2-driver
  • BrowserKitDriver – behat/mink-browserkit-driver
  • ZombieDriver – behat/mink-zombie-driver
  • SeleniumDriver – behat/mink-selenium-driver
  • SahiDriver – behat/mink-sahi-driver
  • WUnitDriver – behat/mink-wunit-driver

Nosotros vamos a usar el GoutteDriver, que es de los más sencillos (no permite la ejecución de javascript, por ejemplo), pero nos basta para nuestros primeros tests. En cualquier caso, podemos cambiar el driver cuando deseemos.

Una vez instalados los paquetes en nuestro proyecto, tenemos que crear un fichero de configuración en la raíz de nuestro proyecto para Behat, behat.yml. Las opciones dependen de la profundidad a la que queramos llegar configurando, aquí podéis ver la documentación de este fichero, pero para nuestro caso, basta con la siguiente información:

# behat.yml
default:
    extensions:
        Behat\MinkExtension:
            base_url:  'http://localhost:8888/testbehat/web/app_dev.php'
            goutte: ~

Tras la instalación con composer, podemos acceder a las opciones de Behat a través de la terminal en la carpeta bin de la siguiente manera:

$ bin/behat -h

Para crear todo lo necesario y empezar a programar nuestros tests, tenemos que ejecutar la siguiente instrucción:

$ bin/behat –init

Veremos que tenemos un nuevo directorio en nuestro proyecto, features, y dentro a su vez, la carpeta bootstrap, con la clase FeatureContext.php. Por ahora no vamos a profundizar en esta clase, pero si que es necesario realizar un paso para que nuestros tests puedan usar Mink, así que lo abrimos, y en la declaración de la clase, modificamos lo siguiente:

// features/FeatureContext.php
[...]
class FeatureContext implements Context

[...]

Por esto:

// features/FeatureContext.php
[...]
use Behat\MinkExtension\Context\MinkContext;

[...]

class FeatureContext extends MinkContext implements Context

[...]

Nuestro primer Test

Nat Geo Wild  GIFs - Find & Share on GIPHY

Bien, llegó el momento, manos a la obra con la creación de nuestro primeros tests sobre una feature. Recordad que comenzamos con funcionalidades sencillas, ya seremos capaces de realizar tests más complicados.

Supongamos que tenemos una entidad “Shop” con su respectivo listado y formulario de creación. Cuando rellenamos correctamente el registro, se nos redirige a la página de detalle de Shop, donde vemos los datos y un enlace a “Edit” y “Back to the list” La tienda tiene los siguientes atributos:

  • name: String, valor no nulo y único.
  • address: String, permite nulos.
  • category: String, valor no nulo.

Queremos validar el formulario de creación de entidad Shop, y partimos desde la ruta de lista de tiendas. Creamos un fichero “createshop.feature” en la carpeta “features”, y definimos al comienzo la feature, para luego definir los distintos escenarios que comprobaremos.

Si nos apoyamos en el punto en el que vimos las historias de usuario, veremos que no nos es desconocido el lenguaje usado para la definición de los escenarios de los tests, pero para analizar por líneas, con “Given” especificamos el contexto, dónde estamos, qué es lo que vemos, etc. Dentro de “When”, efectuamos una secuencia de acciones, como es acceder a enlaces, rellenar los campos de un formulario… Por últimos, en el “Then” definimos las validaciones, los supuestos que hacen que el test se haya efectuado con éxito. Estas palabras “Given-When-Then” son los steps, y Behat se apoya en la clase FeatureContext.php para saber qué hacer con la instrucción que viene a continuación, y aunque no diferencia a nivel de lógica entre las tres, si que nosotros debemos usarlas en el contexto que quieren aportar por adaptarnos a la mentalidad de BDD con Behat.

Rescatamos el ejemplo anterior y añadimos nuevos escenarios:

Feature: Create a new shop
  In order to create a new shop
  As an admin
  I need to be able to add a new shop via create form.

Scenario: Create a new valid shop
  Given I am on "/shop"
  When I follow "Create a new shop"
    And I fill in "Name" with "Test"
    And I fill in "Address" with "Fake road"
    And I fill in "Category" with "Tests"
    And I press "Create"
  Then I should see "Shop"
    And I should see "Test"
    And I should see "Edit"

Scenario: Back to the list
  Given I am on "/shop"
  When I follow "Create a new shop"
    And I follow "Back to the list"
  Then I should see "Shops list"
    And I should not see "Back to the list"

Scenario: Testing required name field
  Given I am on "/shop"
  When I follow "Create a new shop"
    And I fill in "Name" with "      "
    And I fill in "Address" with "Fake road"
    And I fill in "Category" with "Tests"
    And I press "Create"
  Then I should see "Shop creation"
    And I should see "This value should not be blank"

Scenario: Testing required category field
  Given I am on "/shop"
  When I follow "Create a new shop"
    And I fill in "Name" with "Test"
    And I fill in "Address" with "Fake road"
    And I fill in "Category" with "       "
    And I press "Create"
  Then I should see "Shop creation"
    And I should see "This value should not be blank"

Scenario: Testing unique name field
  Given I am on "/shop"
  When I follow "Create a new shop"
    And I fill in "Name" with "Test"
    And I fill in "Address" with "Fake road"
    And I fill in "Category" with "Tests"
    And I press "Create"
  Then I should see "Shop creation"
    And I should see "This value is already used"

Vemos entonces varias instrucciones:

Con “I am on …” nos posicionamos en una ruta de nuestra página, para dar comienzo a las pruebas, debe ser una ruta válida de nuestra aplicación.

Con “I follow …” podemos navegar por enlaces de nuestra página. El valor que acepta para el enlace puede ser el cualquier atributo del enlace entre id|title|alt|text.

Con “I fill in … with …” rellenamos un campo con el valor especificado. La clave para acceder al campo puede ser el id|text|name.

Con “I press …” simulamos la presión de un botón. Podemos acceder al botón a través de id|value.

Por último, con “I should see …”, al igual que con “I should not see …” indicamos elementos que deben ser o no visibles tras los eventos.

Otro punto que podemos observar a través de este ejemplo es una ventaja de este tipo de tests, y es que en todo momento estamos simulando una navegación de un usuario con acciones que realizaría sobre una página, y es independiente de nuestra implementación, que aunque en nuestro caso es Symfony, aquí no aparece en ningún lado, y estos tests servirían también para Laravel, Zend, etc.

Para ejecutar los tests, sólo nos quedará lanzar la siguiente instrucción:

$ bin/behat

Behat recorrerá todos los tests de features en orden alfabético de la carpeta features. Si queremos ejecutar sólo los tests correspondientes a una feature, lanzamos la siguiente instrucción:

$ bin/behat features/createshop.feature

Cerrando…

Con esto, ya tenemos instalado en nuestro proyecto Behat, y hemos sido capaces de realizar los tests concernientes a una funcionalidad sencilla y muy común, como es la creación de una entidad a través de su formulario. Por delante queda un camino largo de aprendizaje y perfección, pero espero que si estás comenzando, te sirva de ayuda esta pequeña introducción.

Más adelante explicaremos cómo extender Gherkin para poder crear nuestras propias instrucciones, y añadir más potencia a nuestras pruebas.

Referencias

Os dejo aquí varias páginas que tratan los temas del post y profundizan más, en caso de querer buscar más datos o para usarlos de referencia.