Desarrollo Ágil

con BDD

Construyendo el producto correcto de manera efectiva

Adrian Moya / @adrianmoya
2013

Agilidad

Capacidad de realizar una tarea o acción
de la manera más rápida y eficiente

Iterativo de Tiempo fijo

Iteraciones cortas y de duración fija predeterminada

Participación activa del cliente

Cada iteración es aprobada por el cliente, la retroalimentación se implementa en la(s) iteraciones siguientes

Dirigido por características

Principio 80/20 para implementar 20% de características que se usen el 80% de las veces

Entrega basada en prioridades

Prioridad a las necesidades del clientes
y el riesgo en el desarrollo

Adaptable

No buscamos remover la incertidumbre, sino adaptarnos a las necesidades cambiantes

Equipos Auto-organizados

Activamente involucrado, con capacidad de toma de decisiones

Más énfasis en las personas y sus habilidades
que en el proceso

Más disciplinado

Más rapidez y calidad
involucra disciplina de equipo y personal

Simplicidad

Énfasis en la simplicidad y abierto al cambio, utilizando tecnologías ligeras

¿Por qué no todos usan ágil?

No son una bala de plata

Es difícil implementar ágil correctamente

BDD nos ayuda a implementar ágil

¿Qué es BDD?

En palabras de Dan North

El uso de ejemplos en múltiples niveles para crear un entendimiento compartido y sacar a flote la incertidumbre para construir software que importa

El uso de ejemplos...

Sucesión de Fibbonaci

Ejemplo: 0, 1, 1, 2, 3, 5, 8...

...en multiples niveles...

A nivel del negocio en su lenguaje natural...


Escenario: listar clientes con deuda por vencer
  Dado que existen los siguientes clientes:
    |cliente | fecha_vencimiento_deuda |
    | Adrian |       30/05/2013        |
    | Carlos |       15/06/2013        |
  Y que la fecha actual es "28/05/2013"
  Cuando solicito la lista de clientes con deuda por vencer
  Entonces debo ver listado a "Adrian"
  Pero no debo ver listado a "Carlos"

						

A nivel del código en el lenguaje de programación


public class MarkdownSpec {

    @Test
    public void it_converts_plain_text_to_html_paragraphs() {
	Markdown markdown = new Markdown();
	String htmlResult = markdown.toHtml("Hi, there"); 
	assertThat(htmlResult, is("

Hi, there

")); } }

...para crear un entendimiento compartido...

...y sacar a flote la incertidumbre...

Paradoja de Schrödinger

...para construir software que importa

¿Qué es software que importa?

Aquel que genera valor a la organización

“Un proyecto genera valor de negocio cuando aumenta o protege la ganancia, o reduce los costos, en alineación con la estrategia de la organización.”
Andy Pols & Chris Matts - Agile 2008

Ciclo de vida BDD

BDD de historias

Explorar el dominio

Nos permite explorar el dominio del negocio en 3 niveles

  1. Narrativas
  2. Posibles escenarios
  3. Detalle de escenarios

Narrativa

Para alcanzar alguna característica que agrega valor de negocio
Como rol en el sistema (beneficiario)
Yo quiero/necesito realizar alguna acción en el sistema

Autenticación

Para poder acceder a mi información financiera
Como cliente del banco
Yo necesito poder autenticarme

Escenarios


Característica: Autenticación

Para poder acceder a mi información financiera
Como cliente del banco
Yo necesito poder autenticarme

  Escenario: Sin tarjeta de débito activa (cliente nuevo)

  Escenario: Tarjeta activa primera vez

  Escenario: Autenticación normal sin seguridad adicional

  Escenario: Autenticación normal con seguridad adicional

  Escenario: ...
						

Detalle del escenario

Expresado en un lenguaje simple:

  • Dado: un cierto contexto
  • Cuando: realizo una acción en el sistema
  • Entonces: el sistema se comporta de cierta manera

Característica: Autenticación

Para poder acceder a mi información financiera
Como cliente del banco
Yo necesito poder autenticarme

  Escenario: Sin tarjeta de débito activa (cliente nuevo)
    Dado un cliente "Adrian" con cedula "12345" y tarjeta inactiva
    Y que me encuentro en la página de autenticación
    Cuando ingreso la cédula "12345"
    Y presiono el botón "Autenticar"
    Entonces debo ver un mensaje que me invite a activar mi tarjeta

						

BDD de historias ayuda a que el equipo de desarrollo tenga un entendimiento del negocio al mismo nivel que el cliente, forzandolo a que conteste las preguntas:

  1. ¿Para quien?
  2. ¿Qué?
  3. ¿Cómo?

Pero más importante...

¿POR QUE?

Java

Herramienta para aplicar BDD de historias

Instalación

Maven pom.xml:



    info.cukes
    cucumber-junit
    1.1.3
    test

						

Clase runner


package com.adrianmoya.bddsample.cucumber;

import cucumber.api.junit.Cucumber;
import org.junit.runner.RunWith;

@RunWith(Cucumber.class)
@Cucumber.Options(format = {"pretty", "html:target/cucumber"}, 
	features = {"src/test/resources/features/"},
	glue = {"com.adrianmoya.bddsample.cucumber"})
public class CucumberIT {
    
}

Clase de definición de pasos


package com.adrianmoya.bddsample.cucumber;

public class StepDefinition {

}
						

Nuestra primera característica


Característica: Autenticación
  
  Para poder acceder a mi información financiera
  Como cliente del banco
  Yo necesito poder autenticarme

  Escenario: Sin tarjeta de débito activa (cliente nuevo)
    Dado un cliente "Adrian" con cedula "12345" y tarjeta inactiva
    Y que me encuentro en la página de autenticación
    Cuando ingreso la cédula "12345"
    Y presiono el botón Autenticar
    Entonces debo ver un mensaje que me invite a activar mi tarjeta
						

Creamos nuestro archivo de característica en src/test/resources/features/Autenticacion.feature

Cucumber nos da ejemplos para implementar los pasos

$ mvn verify
-------------------------------------------------------
 T E S T S
-------------------------------------------------------
Running com.adrianmoya.bddsample.cucumber.CucumberIT


You can implement missing steps with the snippets below:

@Dado("^un cliente \"([^\"]*)\" con cedula \"([^\"]*)\" y tarjeta inactiva$")
public void un_cliente_con_cedula_y_tarjeta_inactiva(String arg1, String arg2) throws Throwable {
    // Express the Regexp above with the code you wish you had
    throw new PendingException();
}

						

Estadísticas de los escenarios

#language: es
Característica: Autenticación
  
  Para poder acceder a mi información financiera
  Como cliente del banco
  Yo necesito poder autenticarme

  Escenario: Sin tarjeta de débito activa (cliente nuevo)           # Autenticacion.feature:9
    Dado un cliente "Adrian" con cedula "12345" y tarjeta inactiva
    Y que me encuentro en la página de autenticación
    Cuando ingreso la cédula "12345"
    Y presiono el botón Autenticar
    Entonces debo ver un mensaje que me invite a activar mi tarjeta
Tests run: 7, Failures: 0, Errors: 0, Skipped: 6, Time elapsed: 1.311 sec

Results :

Tests run: 7, Failures: 0, Errors: 0, Skipped: 6
						

package com.adrianmoya.bddsample.cucumber;

import cucumber.api.PendingException;
import cucumber.api.java.es.Cuando;
import cucumber.api.java.es.Dado;
import cucumber.api.java.es.Entonces;


public class StepDefinition {

    @Dado("^un cliente \"([^\"]*)\" con cedula \"([^\"]*)\" y tarjeta inactiva$")
    public void un_cliente_con_cedula_y_tarjeta_inactiva(String arg1, String arg2) throws Throwable {
        // Express the Regexp above with the code you wish you had
        throw new PendingException();
    }

    @Dado("^que me encuentro en la página de autenticación$")
    public void que_me_encuentro_en_la_página_de_autenticación() throws Throwable {
        // Express the Regexp above with the code you wish you had
        throw new PendingException();
    }
						

Queremos ejercitar el sistema de la manera que lo haría nuestro cliente

...si es una aplicación web, debemos usar un navegador...

Existen 2 tipos de navegadores para pruebas automatizadas

  • Emuladores de navegadores sin interfaz: ej. HtmlUnit, Phantom.js
  • Controlador de navegadores reales: Chrome, Firefox, IE

Selenium WebDriver

Librería de automatización que nos ofrece un API único para controlar distintos tipos de navegadores

Instalación

Maven pom.xml:



    org.seleniumhq.selenium
    selenium-java
    2.33.0
    test

						
http://docs.seleniumhq.org/download/maven.jsp

Configurando el driver


import org.openqa.selenium.WebDriver;
import org.openqa.selenium.htmlunit.HtmlUnitDriver;

public class StepDefinition {

    static final String APP_URL = "http://localhost:8080/javasample/";
    WebDriver driver;

    @Before
    public void setup() {
        this.driver = new HtmlUnitDriver();
    }						

Uso el driver en mis pasos


@Dado("^que me encuentro en la página de autenticación$")
public void que_me_encuentro_en_la_página_de_autenticación() throws Throwable {
    driver.get(APP_URL + "autenticacion/");
}
   
@Cuando("^ingreso la cédula \"([^\"]*)\"$")
public void ingreso_la_cédula(String cedula) throws Throwable {
    WebElement campoCedula = this.driver.findElement(By.id("login_form:documentNumber"));
    campoCedula.sendKeys(cedula);
}    

@Cuando("^presiono el botón Autenticar$")
public void presiono_el_botón_Autenticar() throws Throwable {
    WebElement button = this.driver.findElement(By.id("login_form:loginBtn"));
    button.click();
}
						

Es hora de comenzar a codificar el sistema...

BDD de especificaciones

Diseño emergente

Nos permite describir como interactúan los objetos para resolver un problema, sus roles, responsabilidades y mensajes

  • De manera iterativa
  • A través de ejemplos de codigos

Tradicionalmente: BDUF

  • Difícil de cambiar más adelante
  • Tenemos que pensar en todo antes de codificar
  • Tratamos de que no se nos escape nada

...pero la realidad es...

No estamos construyendo puentes

Costo del cambio

Aumenta exponencialmente en las fases tardías del desarrollo

Ciclo SpecBDD

  • Usamos ejemplos de código para especificar el comportamiento
  • Usamos dobles para especificar el intercambio de mensajes

Más facil decirlo que hacerlo

BDD de especificaciones es una técnica
que requiere mucha práctica

JUnit

Instalación

Maven pom.xml:



  junit
  junit
  4.11
  test

						

Describimos un objeto mediante una clase


package com.adrianmoya.bddsample;

public class MyObjectSpec {

     @Test
     public void it_returns_foo_when_i_call_bar() {
	 //Dado
         MyObject myObject = new MyObject();
         
	 //Cuando
         String result = myObject.bar();
         
	 //Entonces
         assertThat(result, is("foo"));
     }
}
						

Ejecutamos las especificaciones

-------------------------------------------------------
 T E S T S
-------------------------------------------------------
Running com.adrianmoya.bddsample.MyObjectSpec
Tests run: 1, Failures: 1, Errors: 0, Skipped: 0, Time elapsed: 0.088 sec <<< FAILURE!
it_returns_foo_when_i_call_bar(com.adrianmoya.bddsample.MyObjectSpec)  Time elapsed: 0.02 sec  <<< FAILURE!
java.lang.AssertionError: 
Expected: is "foo"
     but: was null
	at org.hamcrest.MatcherAssert.assertThat(MatcherAssert.java:20)
	at org.junit.Assert.assertThat(Assert.java:865)
	at org.junit.Assert.assertThat(Assert.java:832)
	at com.adrianmoya.bddsample.MyObjectSpec.it_returns_foo_when_i_call_bar(MyObjectSpec.java:15)

Nuestro código actual


package com.adrianmoya.bddsample;

class MyObject {

    public MyObject() {
    }

    String bar() {
        return null;
    }
    
}
						

Implementamos el comportamiento esperado


package com.adrianmoya.bddsample;

class MyObject {

    public MyObject() {
    }

    String bar() {
        return "foo";
    }
    
}
						

Ejecutamos las especificaciones

-------------------------------------------------------
 T E S T S
-------------------------------------------------------
Running com.adrianmoya.bddsample.MyObjectSpec
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.047 sec

Results :

Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
						

Librería para construir dobles en Java

Instalación

Maven pom.xml:



    org.mockito
    mockito-core  
    1.9.5 
    test

						

Agregamos una especificación


public class MyObjectSpec { 

 @Test
 public void it_sends_a_message_to_collaborator(){
    //Dado
    MyCollaborator myCollaborator = mock(MyCollaborator.class);
    MyObject myObject = new MyObject(myCollaborator);

    //Cuando
    myObject.bar();

    //Entonces
    verify(myCollaborator).sendMessage("Mensaje");
 }
						

Ejecutamos las especificaciones

-------------------------------------------------------
 T E S T S
-------------------------------------------------------
Running com.adrianmoya.bddsample.MyObjectSpec
Tests run: 2, Failures: 1, Errors: 0, Skipped: 0, Time elapsed: 0.227 sec <<< FAILURE!
it_sends_a_message_to_collaborator(com.adrianmoya.bddsample.MyObjectSpec)  Time elapsed: 0.18 sec  <<< FAILURE!
org.mockito.exceptions.verification.WantedButNotInvoked: 
Wanted but not invoked:
myCollaborator.sendMessage("Mensaje");
-> at com.adrianmoya.bddsample.MyObjectSpec.it_sends_a_message_to_collaborator(MyObjectSpec.java:32)
Actually, there were zero interactions with this mock.

	at com.adrianmoya.bddsample.MyObjectSpec.it_sends_a_message_to_collaborator(MyObjectSpec.java:32)

Implementamos el comportamiento esperado


class MyObject {
    
    MyCollaborator collaborator;

    public MyObject() {
        this.collaborator = new MyCollaborator();
    }

    MyObject(MyCollaborator myCollaborator) {
        this.collaborator = myCollaborator;
    }

    String bar() {
        this.collaborator.sendMessage("Mensaje");
        return "foo";
    }
						

Ejecutamos las especificaciones

-------------------------------------------------------
 T E S T S
-------------------------------------------------------
Running com.adrianmoya.bddsample.MyObjectSpec
Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.248 sec

Results :

Tests run: 2, Failures: 0, Errors: 0, Skipped: 0

Repetir

  • Hasta que todas las especificaciones esten en verde
  • Hasta que los escenarios de BDD de historia esten en verde

Recursos

Sitio de la comunidad - http://agilescolombia.org/

Próximos eventos - http://www.meetup.com/AgilesColombia/

Foro - https://groups.google.com/forum/?fromgroups#!forum/agiles-colombia

Sitio web - http://sparkta.co/

Entrenamiento, consultoria, talleres en temas ágiles

Adrian Moya

@adrianmoya

http://adrianmoya.com