Contenido

JBehave

Apesar de mi mal comienzo con JBehave , debo decir que le voy cogiendo el tranquillo.

JBehave consiste en un sistema para hacer BDD en Java. En otras palabras: permite definir en un lenguaje no formal el comportamiento de la aplicación, utilizando expresiones regulares para transformarlo en un lenguaje formal.

Java

Ejemplo

JBehave tiene cosas que no me gustan nada, pero también tiene otras que me gustan mucho. Veamos primero un ejemplito lo más pequeño que he sido capaz de hacerlo:

pom.xml

Comenzaremos con el archivo POM necesario:

 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
<project xmlns="https://maven.apache.org/POM/4.0.0" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="https://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.magmax</groupId>
    <artifactId>jbehaveexample</artifactId>

    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>jbehaveexample</name>
    <url>https://maven.apache.org</url>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

    </properties>

    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.10</version>
            <scope>test</scope>

        </dependency>
        <dependency>
            <groupId>org.jbehave</groupId>
            <artifactId>jbehave-maven-plugin</artifactId>
            <version>3.5.4</version>
        </dependency>
    </dependencies>
</project>

Como veréis, he añadido la dependencia de JBehave.

src/test/resources/org/magmax/jbehaveexample/my_example.story

1
2
3
4
5
6
7
8
9
In order to show JBehave behaviour
As a newbie
I want to show this example

Given nothing special
When field operand1 contains 1
And field operand2 contains 2
And I press on "sum"
Then it returns 3

Como véis, parece una mini calculadora. Según la descripción, tendremos un campo “operand1” y otro “operand2”, que parece que pueden contener enteros. Además, tiene que haber un botón “sum” y, en alguna parte, mostrar un “3”. Admito todo tipo de críticas al respecto, pero recordad que se trata de algo sencillito.

src/test/java/org/magmax/jbehaveexample/MyExample.java

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
package org.magmax.jbehaveexample;

import org.jbehave.core.configuration.Configuration;
import org.jbehave.core.junit.JUnitStory;
import org.jbehave.core.reporters.Format;
import org.jbehave.core.reporters.StoryReporterBuilder;

public class MyExample extends JUnitStory {
    @Override
    public Configuration configuration() {
        Configuration result = super.configuration();
        result.useStoryReporterBuilder(getStoryReporterBuilder());
        return result;
    }

    private StoryReporterBuilder getStoryReporterBuilder() {
        StoryReporterBuilder result = new StoryReporterBuilder();
        result.withFormats(Format.CONSOLE, Format.HTML, Format.STATS);
        return result;
    }
}

Éste es el archivo de historia. Será necesario uno por cada historia (aunque hay maneras de necesitar menos). Lo que se hace es indicar la configuración necesaria para probar la historia de forma correcta. Como véis, lo único que hago es establecer la configuración por defecto, indicando que quiero las salidas de Consola, Html y estadísticas. Realmente nos bastaba con Consola, pero así véis más cosas.

Primera ejecución

Con esto ya podemos ejecutar el ejemplo. Evidentemente fallará, pero debe dar un resultado similar al siguiente:

 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
44
45
46
47
48
49
50
51
52
53
Processing system properties {}
Using controls EmbedderControls[batch=false,skip=false,generateViewAfterStories=true,ignoreFailureInStories=false,ignoreFailureInView=false,storyTimeoutInSecs=300,threads=1]

(BeforeStories)

Using 1 threads
Using executor service java.util.concurrent.ThreadPoolExecutor@64883c
Running story org/magmax/jbehaveexample/my_example.story

(org/magmax/jbehaveexample/my_example.story)
Scenario: In order to show JBehave behaviour
As a newbie
I want to show this example
Given nothing special (PENDING)
When field operand1 contains 1 (PENDING)
And field operand2 contains 2 (PENDING)
And I press on "sum" (PENDING)
Then it returns 3 (PENDING)
@Given("nothing special")
@Pending
public void givenNothingSpecial(){
  // PENDING
}

@When("field operand1 contains 1")
@Pending
public void whenFieldOperand1Contains1(){
  // PENDING
}

@When("field operand2 contains 2")
@Pending
public void whenFieldOperand2Contains2(){
  // PENDING
}

@When("I press on \"sum\"")
@Pending
public void whenIPressOnsum(){
  // PENDING
}

@Then("it returns 3")
@Pending
public void thenItReturns3(){
  // PENDING
}


(AfterStories)

Generating reports view to 'jbehaveexample/target/jbehave' using formats '[console, html, stats]' and view properties '{defaultFormats=stats, decorateNonHtml=true, viewDirectory=view, decorated=ftl/jbehave-report-decorated.ftl, reports=ftl/jbehave-reports-with-totals.ftl, maps=ftl/jbehave-maps.ftl, navigator=ftl/jbehave-navigator.ftl, views=ftl/jbehave-views.ftl, nonDecorated=ftl/jbehave-report-non-decorated.ftl}'
Reports view generated with 3 stories (of which 1 pending) containing 1 scenarios (of which 0 failed and 1 pending)

Lo más importante es fijarse en la parte que nos muestra el código java que necesitamos para nuestros “steps”. Así que lo copiamos y lo pegamos en el siguiente archivo (rellenándolo y quitando los @Pending):

 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
package org.magmax.jbehaveexample;

import org.jbehave.core.annotations.Given;
import org.jbehave.core.annotations.Then;
import org.jbehave.core.annotations.When;
import static org.junit.Assert.*;

public class MyExampleSteps {
    int operand1 = 0;
    int operand2 = 0;
    int result = 0;

    @Given("nothing special")
    public void givenNothingSpecial() {
    }

    @When("field operand1 contains $value")
    public void whenFieldOperand1ContainsAValue(int value) {
        operand1 = value;
    }

    @When("field operand2 contains $value")
    public void whenFieldOperand2ContainsAValue(int value) {
        operand2 = value;
    }

    @When("I press on \"sum\"")
    public void whenIPressOnsum() {
        // Llamar a la calculadora real
        result = operand1 + operand2;
    }

    @Then("it returns $value")
    public void thenItReturns3(int value) {
        assertEquals(value, result);
    }
}

Fijáos también que he realizado algunas modificaciones a la plantilla que me daba JBehave: he sustituido los valores por variables y he añadido parámetros en mis funciones. Las variables y los parámetros siempre coinciden en nombre, aunque las variables comienzan por el símbolo del dólar.

Fijáos también en el método whenIPressOnsum; aquí es donde iría la llamada a mi clase real, pero por abreviar, he puesto ahí mismo el código.

El caso es que esto no me funciona. No sé si es un problema de nomenclatura (asumo que sí), pero sé cómo arreglarlo: modificando nuestra clase que hereda de JUnitStory, de manera que sepa encontrar este archivo. Finalmente, quedaría así:

 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
package org.magmax.jbehaveexample;

import java.util.List;
import org.jbehave.core.configuration.Configuration;
import org.jbehave.core.junit.JUnitStory;
import org.jbehave.core.reporters.Format;
import org.jbehave.core.reporters.StoryReporterBuilder;
import org.jbehave.core.steps.CandidateSteps;
import org.jbehave.core.steps.InstanceStepsFactory;

public class MyExample extends JUnitStory {

    @Override
    public List candidateSteps() {
        InstanceStepsFactory stepsFactory = new InstanceStepsFactory(configuration(), new MyExampleSteps());
        return stepsFactory.createCandidateSteps();
    }

    @Override
    public Configuration configuration() {
        Configuration result = super.configuration();
        result.useStoryReporterBuilder(getStoryReporterBuilder());
        return result;
    }

    private StoryReporterBuilder getStoryReporterBuilder() {
        StoryReporterBuilder result = new StoryReporterBuilder();
        result.withFormats(Format.CONSOLE, Format.HTML, Format.STATS);
        return result;
    }
}

Y, con este cambio, comienza a andar.

Aciertos y errores

Como bien dije, hay cosas que me gustan mucho y otras que nada. Para empezar, está el objeto MostUsefulConfiguration. Se supone que es la configuración más útil, pero… ¿qué es lo más útil? Puede que sea la más común, pero lo cierto es que no incluye el formato “Console” y, como consecuencia, no imprime nada por pantalla. Eso me obliga a tener que sobreescribir el método de Configuración, como en el ejemplo.

Un acierto son las variables en las expresiones regulares, ya que JBehave es suficientemente listo como para resolverlas él solo. Quizá sea un poco peligroso en algún caso darle tanta inteligencia, pero en la mayoría de ellos viene muy bien.

Otra cosa que no me gusta es tener que indicarle el objeto que implementan los pasos a seguir. Debería encontrarse este objeto, basándose en la nomenclatura, de la misma manera que se ha encontrado el archivo .story.

Y otra cosa que no me gusta es tener que estar creando los archivos de historia, aunque siempre se puede crear uno genérico que permita encontrar todas las historias, aunque eso me ha dado no pocos dolores de cabeza.

Disculpas

En previsión de que me digáis que no tengo razón, abro ya este apartado. A penas he hecho 2 ejercicios con JBehave, así que es normal que me falten conocimientos y es posible que los problemas que he encontrado se puedan arreglar fácilmente. Me encantaría que me dijerais cómo :D

Más información

La mayor parte de la información la podéis obtener en la propia página de JBehave reference, aunque el JavaDoc está a medias aún.