Cucumber es un framework que utiliza el lenguaje Gherkin para especificar las pruebas.

Estructura del proyecto

Los ficheros con las features deben estar en el classpath de los test.

Para poder ejecutar escenarios de cucumber de mediante JUnit es necesario configurar una clase Runner, la cual contendrá:

@Suite
@IncludeEngines("cucumber")
@SelectClasspathResource("ubicacion")
@ConfigurationParameter(key = GLUE_PROPERTY_NAME, value = "es.um.atica.[GRUPO].[CONTEXT].cucumber")
@TestPropertySource("classpath:test.properties")
public class TfCucumberTests {
}

Las cuales indican:

  • @IncludeEngines: Indicamos que son test basados en Cucumber.
  • @SelectClassPathResource: Ubicacion de los ficheros features dentro del classpath.
  • @ConfigurationParameter(GLUE_PROPERTY_NAME): El paquete común para todas las clases de test.
  • TestPropertySource("classpath:test.properties"): Ubicación del fichero de properties de los tests.

Features

Su propósito es describir los casos de prueba y agrupar escenarios relacionados. Se escriben en lenguaje Gherkin que sigue una estructura muy cercana al lenguaje natural.

Para describir los escenarios se utilizan algunos conceptos y palabras claves:

  • Característica: Tiene el propósito de agrupar escenarios y hacer una descripción a alto nivel de los mismos. 
  • Antecedentes: Describe las precondiciones que se tienen que dar en los escenarios.
  • Escenario: Es un ejemplo concreto que concreta una regla de negocio. Además representa un test que se ejecuta mediante Cucumber. Se divide en varias etapas secuenciales:
    • Dado: Contexto inicial/precondiciones que se cumplen en el inicio del test
    • Cuando: Describe un evento.
    • Entonces: Salida esperada tras la ejecución del evento.
  • Dentro de cada etapa se pueden utilizar las keywords Y/Pero para unir sucesivas condiciones.
  • Esquema del escenario: Se utiliza para ejecutar el mismo escenario N veces con distintos valores. Se combina con la palabra clave Ejemplos.
  • Ejemplos: Datos en forma tabular que se inyectan en el escenario. La primera fila son los nombres de las variables y las siguientes filas los valores de las mismas. En el ejemplo la primera ejecución reemplaza la variable de la primera fila con el valor de la segunda fila y la segunda ejecución con el valor de la tercera fila, etc...
  • Argumentos en Etapas
    • Doc Strings: Permite pasar parámetros a la definición de la etapa. Permite reutilizar la etapa en distintos escenarios.
  • Tablas de datos: Permite pasar una lista de valores a la definición de la etapa. Estos valores se definen separados por " | "

Es buena práctica que la descripción en lenguaje natural sea concisa. Debe evitar referencias a la implementación. El comportamiento de implementación lo daremos en los StepDefinition a nivel de clases Java. Un ejemplo de ello seria:

@tf @unauthorized @error

Escenario: Obtener info de mi TF sin autenticar

Dado un usuario no autenticado

Cuando trata de obtener un listado de usuarios

Entonces obtiene un error de autenticación

Se puede consultar mas información y buenas practicas.

Step definitions

Es un método en java con una expresión que está vinculado con una o varias Steps de Gherkin. Cuando Cucumber procesa un determinado fichero de features busca por la definición correspondiente del step definition y lo ejecuta.

Una vez creados los ficheros feature si ejecutamos los test unitarios el propio framework nos proporciona en la salida una propuesta de los métodos a implementar (Cuidado con los acentos y la "ñ" incluidos en el fichero features).

Siguiendo el ejemplo anterior crearemos la siguiente clase dentro del paquete "es.um.atica.acade.tf.cucumber":

@CucumberContextConfiguration
public class TfCucumberSteps extends CucumberSpringConfiguration {
    
    protected MvcResult mvcResult;

    @io.cucumber.java.Before
    public void setup() { super.setup(); }

    @Dado("un usuario no autenticado")
    public void un_usuario_no_autenticado() { setJWT(); }
    
    @Cuando("trata de obtener mi TF")
    public void trata_de_obtener_mi_TF() throws Exception {
        mvcResult = getMVC().perform(MockMvcRequestBuilders.get(getAPIPath())
            .with(getJWT())
            .accept(MediaType.APPLICATION_JSON_VALUE)).andReturn();
    }

    @Entonces("obtiene un error de autenticación")
    public void obtiene_un_error_de_autenticacion() {
        assertEquals(401,mvcResult.getResponse().getStatus());
    }
}

Las anotaciones de la librería @Dado, @Cuando, @Entonces junto con el texto de las mismas son las utilizadas por el framework para hacer el matcheo con la especificación de los ficheros feature.

Dentro de cada método se implementa la lógica correspondiente a la etapa. Para facilitar las cosas podemos crear una clase genérica de "Configuración" que contenga todos los atributos y configuración necesarias para la ejecución de las etapas de cucumber. En nuestro ejemplo "CucumberSpringConfiguration".

Step definitions con parámetros

Se pueden utilizar expresiones regulares o expresiones de Cucumber para capturar partes de la etapa en Gherkin y pasarlo como parámetros a los step definitions. Veamos un ejemplo:

.features:

Escenario: Obtener info de mi TF autenticado con TF
Dado el usuario autenticado "alumnoTf1@um.es"
Cuando trata de obtener mi TF
Entonces obtiene una respuesta correcta
Y contiene información mi TF
Y el titulo del TF es "Mi super TF"

.java:

    @Y("el titulo del TF es {string}")

    public void miTfWithTitle(String titulo) throws Exception {

        EntityModel<miTfDTO> miTF = objectMapper.readValue(mvcResult.getResponse().getContentAsString(), new TypeReference<EntityModel<miTfDTO>>() {});

        assertEquals(titulo,miTF.getTitulo());

    }

El método será el seleccionado para ejecutar cualquier step en los ficheros feature que haga match con la expresión indicada. Además se encarga de hacer la conversión de tipos entre el String del fichero feature y el tipo deseado.

Step definitions con data tables

Para mapear datos en los ficheros de feature con forma tabular el framework proporciona la clase DataTable. Dispone de una api completa para utilizar de distintas formas de utilizar los datos:

.features:

@tf @MiTf @detail @success
Escenario: Obtener info de mi TF autenticado con TF
Dado el usuario autenticado "alumnoTf1@um.es"
Cuando trata de obtener mi TF
Entonces obtiene una respuesta correcta
Y contiene información mi TF
Y la informacion del TF es
| Mi super TF | Victor Gomollon | 9 |

.java

   @Y("la informacion del TF es ")

    public void miTfWithTitle(DataTable dataTable) throws Exception {

        EntityModel<miTfDTO> miTF = objectMapper.readValue(mvcResult.getResponse().getContentAsString(), new TypeReference<EntityModel<miTfDTO>>() {});

        assertEquals(dataTable.asLists().get(0),miTF.getTitulo());

        assertEquals(dataTable.asLists().get(1),miTF.getAlumno());

        assertEquals(dataTable.asLists().get(2),miTF.getCalificacion());

    }

Hooks

Son bloques de código que pueden ejecutarse en distintos puntos del ciclo de vida de Cucumber. Un uso típico que se les da es para configurar pasos previos y posteriores a cada escenario. Alguno ejemplos serian:

  • La anotación @After hace que el método se ejecute después de cada escenario.
  • La anotación @AfterStep es para que el método se ejecute a continuación de cada step.
  • La anotación @Before hace que el metodo se ejecute antes de cada escenario.
  • La anotación @BeforeStep es para que el método se ejecute a antes de cada step.

Tags

Permiten organizar features y escenarios de Cucumber. Se pueden utilizar con distintos propósitos:

Se definen en los ficheros feature:

@tf @MiTf @unauthorized @error

Escenario: Obtener info de mi TF sin autenticar

Dado un usuario no autenticado

Cuando trata de obtener mi TF

Entonces obtiene un error de autenticación

Podemos emplear dicho tag para no ejecutar en un momento dado todos los escenarios que vayan anotados con él, en el starter de JUnit.

@Suite
@IncludeEngines("cucumber")
@SelectClasspathResource("ubicacion")
@ConfigurationParameter(key = GLUE_PROPERTY_NAME, value = "es.um.atica.[GRUPO].[CONTEXT].cucumber")

@ConfigurationParameter(key = FILTER_TAGS_PROPERTY_NAME, value = "not @unauthorized")
@TestPropertySource("classpath:test.properties")
public class TfCucumberTests {
}


O con mayor prioridad mediante la instrucción en línea de comandos:

mvn test -Dcucumber.filter.tags="not @unauthorized"

Ejecución de escenarios

Podemos ejecutar:

  • A través del IDE. En la clase runner RunCucumberTest menú contextual Run As -> JUnitTest. 
  • Por línea de comandos, con maven "mvn test".

Con cualquiera de las opciones se va ejecutando un browser para cada escenario. En él se ejecutan las pruebas programadas.

Mejoras en tiempos de ejecución

Por defecto los test de cada escenario de prueba se ejecutan de forma secuencial.  Con JUnit 5 podemos configurar que se ejecuten los escenarios en paralelo. 

Una posibilidad es añadir en el classpath un fichero junit-platform.properties con la siguiente propiedad:

cucumber.execution.parallel.enabled=true

El resto de opciones de configuración se pueden consultar en la documentación.

Informes

Cucumber se puede configurar para obtener informes en distintos formatos. Se realiza mediante la propiedad PLUGIN_PROPERTY_NAME

@ConfigurationParameter(key = PLUGIN_PROPERTY_NAME, value = "html:target/cucumber.html,json:target/cucumber.json,junit:target/cucumber.xml, com.aventstack.extentreports.cucumber.adapter.ExtentCucumberAdapter:")


o a través de un fichero de configuración "junit-platform.properties" ubicado en el mismo path que el fichero de properties de tests.

cucumber.publish.quiet=true

cucumber.publish.enabled=false

cucumber.plugin=pretty, html:target/cucumber-reports/Cucumber.html, json:target/cucumber-reports/Cucumber.json, junit:target/cucumber-reports/Cucumber.xml

  • html:target/cucumber.html 

Genera un informe en html básico en la carpeta y con el nombre indicado. En él podemos ver estadísticas agregadas sobre los test lanzados. Nos proporciona información de cada feature y etapa indicado si se ha ejecutado correctamente o ha fallado.

  • json:target/cucumber.json / junit:target/cucumber.xml

Misma información pero en formato json o xml. Se puede utilizar como entrada estructurada para otras herramientas de análisis.

Es recomendable utilizar extend reports. Se activa indicando  ExtentCucumberAdapter. La salida es en formato html y añade la traza de error vinculada al step. También la captura de pantalla del momento previo al fallo. Esta información facilita trazar los errores en entornos reales con muchos escenarios (Inicialmente NO se usará).


  • Sin etiquetas