A pesar 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.
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:
4.0.0 org.magmax jbehaveexample 1.0-SNAPSHOT jar jbehaveexample http://maven.apache.org UTF-8 junit junit 4.10 test org.jbehave jbehave-maven-plugin 3.5.4
Como veréis, he añadido la dependencia de JBehave.
src/test/resources/org/magmax/jbehaveexample/my_example.story
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
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:
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 '/home/miguel/Downloads/master_java/ejercicios/practicas/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):
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í:
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, aunque el JavaDoc está a medias aún.
Hola como estas, tendrias mas info de como empezar con jbehave? estoy intentando hacer un ejemplo pero no me sale, o tendrias el fuente de este ejemplo? espero tu respuesta, gracias.
ResponderSuprimirEhm... sí, está todo aquí: http://magmax9.blogspot.com/2012/01/jbehave.html
ResponderSuprimir:D
una consulta, el ejemplo se puede hacer sin maven? para probarlo asi limpio sin otros frameworks ni nada?
ResponderSuprimirSí, claro que se puede hacer sin Maven. Pero Maven no es un framework. Podría decirse que Maven es una herramienta de construcción, aunque es bastante más, como ya expliqué en la introducción a Maven.
SuprimirAl probarlo con Maven lo estás probando limpio :D Maven sólo construirá y resolverá las dependencias. Al final tendrás el JAR, con el que puedes trabajar habitualmente.
Hola de nuevo MagMax, creo que faltaria la parte del candidatesteps, que no logro hacerlo andar jejejeje, por lo menos sale hasta la parte donde ejecutamos por primera vez y sale el error que comentas.
ResponderSuprimirHola de nuevo.
SuprimirPues basta con la parte de:
new InstanceStepsFactory(configuration(), new MyExampleSteps());
Y con eso ya tienes un objeto al que pedirle los CandidateSteps :D
La clase que hereda de JUnitStory es un incordio, ya que lo único que hace es "enlazar" los "Steps" con el archivo ".Story". La verdad es que creo que si hubieran usado una buena convención de nombres, se lo podrían haber ahorrado.
Pero eso es cosa de JBehave... Y todo puede ser que lo tenga, pero que yo no lo conozca.
Muy bueno el tutorial, me saco muchas dudas, pero no logro hacer el ingreso de numero y verificar si esta bien. Espero sigas con esto, que aprendo mucho de vos. gracias
ResponderSuprimirBien... Temo que eso sea culpa mía.
SuprimirHe usado Netbeans para hacerlo. Y resulta que he ido lanzando los tests uno a uno desde el propio IDE.
Aún me faltaría investigar cómo hacer para que se lancen automáticamente. Quizá la solución sea hacer una test Suite con los tests de jbehabe.
Prometo modificar el archivo para mostrar cómo hacerlo.
Un saludo.
Hola de nuevo MagMax, sacame de una duda, por lo que voy entendiendo, jbehave se usaria mas para cuando tenemos los numeros finales que necesitamos y con esto verificamos que lo hagan de forma correcta? por ejemplo lo de la calculadora, al ingresar dos numeros estariamos diciendo que si o si tiene que tener un resultado por ejemplo 5? por que nuestro caso de uso asi lo requeriria? o me estoy confundiendo?
ResponderSuprimirHola de nuevo.
ResponderSuprimirPues... te equivocas XD
JBehave se usa para PRUEBAS DE ACEPTACIÓN. El ejemplo lo he hecho con cosas sencillas, pero puedes complicarlo. Está claro que en una prueba de aceptación no se esperan palabras complejas, ya que el archivo .Story debería poder enseñársele a un cliente.
Un test de aceptación válido podría ser:
Given una tabla hash que enlaza "magmax" y "http://www.magmax.org"
When escribo "magmax" en la casilla de búsqueda
And pulso el botón de búsqueda
Then el recuadro de soluciones muestra "http://www.magmax.org"
Y te reto a que lo implementes :D
Ok estoy investigando sobre las pruebas de aceptacion, perdon si soy muy novato, pero de una me dieron para investigar esta herramienta, yo acostumbrado a Java EE jsf spring jejejeje, pero me gusta aprender jejeje.
SuprimirInteresante el reto jejeje, lo voy a intentar hacer
ResponderSuprimirHola nuevamente Max! te hago una consulta, tendrias por ahi un pdf con informacion mas amplia de JBehave? Espero tu respuesta.
ResponderSuprimirHola!
SuprimirPues... no. Mira en la web de JBehave.
Por otro lado... te debo algo, el mejorar la ejecución de los tests, pero te aseguro que últimamente no tengo tiempo para nada... Temo que tendrá que esperar un mes al menos.
Este comentario ha sido eliminado por un administrador del blog.
ResponderSuprimir