Configuración de Cucumber.js y Jest-Cucumber en Visual Studio Code con TypeScript

0
5158

Antes de empezar recordar que este tutorial forma parte de una cadena de tutoriales en las que pretendo simplemente probar tecnologías actuales. Aquí abajo podrás encontrarlos :

 

También  me gustaría que vieras este video de 2 minutos para entender dónde estamos y a dónde vamos


Mi equipo es Mac

macOS Catalina
Versión 10.15.2

MacBook Pro (15-inch, 2018)
Procesador 2,9 GHz Intel Core i9 de 6 núcleos
Memoria 32 GB 2400 MHz DDR4
Gráficos Intel UHD Graphics 630 1536 MB


En el último tutorial de la cadena (https://www.adictosaltrabajo.com/2020/03/18/el-juego-de-la-vida-en-typescript-con-tdd-y-medida-de-cobertura-en-jest/), en el que veíamos el juego de la vida, aprendidos como podemos hacer TDD con TypeScript.

Lo suyo, cuando construimos software, es que definamos un proyecto en base a historias de usuario y que cada historia, para estar Ready tenga unos criterios de aceptación.

Estos criterios de aceptación deben estar escritos por personal no técnico (en Product Owner o en su lenguaje). Sería interesante investigar un poco los conceptos de ATDD y BDD.

Vamos a verlo con un ejemplo sencillo instalando Cucumber en nuestro proyecto en Visual Studio Code y TypeScript. Nos va a permitir escribir los test en lenguaje natural (aunque no quita entender lo que hay detrás).

Tengo que darle las gracias a Elliot DeNolf porque con su tutorial es francamente sencillo resolver la primera parte, instalar Cucumber. Voy a procurar complementarlo un poquito.

https://dev.to/denolfe/cucumber-js-with-typescript-44cg

Lo primero que voy a hacer es instalar una extensión de Visual Studio Code llamada Cucumber (Gherkin) Full Support. Con ella vamos a manejar los ficheros de un modo más cómodo.

Usaré un fichero de texto (comandos.txt) donde almacenar los comandos que vamos usando en el terminal.

Para instalar estas son las instrucciones:

Instalar cucumber

npm i -D cucumber cucumber-tsflow cucumber-pretty ts-node typescript chai

npm i -D @types/cucumber @types/chai

Tendríamos que investigar un poquito más esto de las vulnerabilidades (os dejo el comando).

npm audit fix

Para que nos funcione Cucumber tenemos que habilitar las anotaciones en nuestro proyecto a través de la activación de “Decorators”. Os recomiendo visitar el link: https://www.typescriptlang.org/docs/handbook/decorators.html

Nos vamos al fichero tsconfig.json y activamos el parámetro: “experimentalDecorators : true”.

Una vez instalado Cucumber, la extensión y los decoradores creamos una carpeta features y dentro un fichero calculadora.feature, que tendrá el DSL de Gherkin con los escenarios de prueba escritos en lenguaje de alto nivel.

Establecemos las condiciones iniciales con Given, lo que sucede con When y lo que queremos comprobar con Then. También, si queremos encadenar operaciones, podemos usar And.

# calculadora.feature
Feature: Operaciones básicas

    Scenario: Suma simple
        Given Un valor de partida de 200
        When se le suma 100
        Then el resultado debe ser 300

    Scenario: Resta simple
        Given Un valor de partida de 200
        When se le resta 100
        Then el resultado debe ser 100

    Scenario: Operaciones múltiples
        Given Un valor de partida de 200
        When se le suma 300
        And se le resta 400
        Then el resultado debe ser 100

Una vez escrito nuestro fichero de alto nivel tenemos que enlazarlo con el código. Si os fijáis, es una expresión textual con un valor en uno o varios puntos.

import { binding, given, then, when } from "cucumber-tsflow";
import { assert } from "chai";

class calculadoraImp {
  resultado: number = 0;

  suma(operando: number): void {
    this.resultado += operando;
  }
  resta(operando: number): void {
    this.resultado -= operando;
  }
}

@binding()
export class calculadora {
  private cal: calculadoraImp = new calculadoraImp();

  @given(/Un valor de partida de (d*)$/)
  public dadoPrimerOperador(cantidad: number) {
    this.cal.resultado = Number(cantidad);
  }

  @when(/se le suma (d*)$/)
  public suma(cantidad: number) {
    this.cal.suma( Number(cantidad));
  }

  @when(/se le resta (d*)$/)
  public resta(cantidad: number) {
    this.cal.resta( Number(cantidad));
  }

  @then(/el resultado debe ser (d*)$/)
  public resultadoDebeSer(cantidadEsperrada: number) {
    assert.equal(this.cal.resultado, cantidadEsperrada);
  }
}

Y en el fichero package.json añadimos un nuevo comando para lanzar Cucumber.

«lanza cucumber»: «./node_modules/.bin/cucumber-js -p default»,

Este es el aspecto de nuestro fichero de comando.

Y ejecutando el comando vemos como se mezclan las variables del test de alto nivel con las llamadas al patrón de código.

Lo único a destacar es que hay que ser muy estricto con los patrones para que funcione bien: mayúsculas, minúsculas, etc..

Bueno, con esto podemos ver que podemos escribir test de alto nivel y enlazarlo con código de bajo nivel con facilidad.

En los tutoriales anteriores usábamos el Framework de test Jest para probar.

Parece que tiene sentido no cambiar el modo de trabajar.

Por suerte disponemos del paquete jest-cucumber que nos permite enlazar las dos cosas: https://www.npmjs.com/package/jest-cucumber

Las instrucciones están muy bien, os invito a visitar su página.

Instalamos.

Modificamos el fichero jest.config.js para incluir otros patrones de ficheros (steps).

Hay que escribir el código de enlace entre las dos cosas, el fichero de escenarios y fuente. Es lo que podéis ver en esta captura de la documentación:

Vamos a instalar ahora otra extensión llamada Jest-cucumber Code generador que nos va a generar el código particular del test automáticamente.

Una vez instalada la extensión, sobre el fichero calculadora.feature marcamos un escenario y con el botón derecho pulsamos la opción “Generate code from feature”.

Y nos genera las estructura de enlace que necesitamos.

test('Multiplicación simple', ({
  given,
  when,
  then
}) => {
  given(/^Un valor de partida de (.*)$/, (arg0) => {

  });

  when(/^se le multiplica por (.*)$/, (arg0) => {

  });

  then(/^el resultado debe ser (.*)$/, (arg0) => {

  });
});

Este es el código completo que se encuentra en la carpeta de test, llamado cucumber.test.ts.

Completamos las funciones.

import { defineFeature, loadFeature } from "jest-cucumber";

const feature = loadFeature("./features/calculadora.feature");

class calculadora {
  resultado: number = 0;

  suma(operando: number): void {
    this.resultado += operando;
  }
  resta(operando: number): void {
    this.resultado -= operando;
  }
}

defineFeature(feature, test => {
  test("Resta simple", ({ given, when, then }) => {
    let cal: calculadora = new calculadora();

    given(/^Un valor de partida de (.*)$/, arg0 => {
      cal.resultado = Number(arg0);
    });

    when(/^se le resta (.*)$/, arg0 => {
      cal.resultado -= Number(arg0);
    });

    then(/^el resultado debe ser (.*)$/, arg0 => {
      expect(cal.resultado).toBe(Number(arg0));
    });
  });
});

Y lanzamos con Jest como cualquier otro test existente. Vemos lo sencillo que es.

Una de las cosas que podemos hacer es usar las palabras clave en castellano para modelar escenarios.

Solo tenemos que decir en el fichero calculadora.feature que el lenguaje es Español: “es” con #language: es

La única pega que os vais a encontrar son los conflictos a la hora de generar el código, que tenéis que seguir poniendo los patrones en Ingles (supongo que también habría parche para eso, pero no merece la pena).

Una de as cosas interesantes de trabajar con Cucumber es la posibilidad de definir tablas de datos (incluso anidadas como arrays de mapas) ver :https://github.com/cucumber/cucumber-js/blob/master/docs/support_files/data_table_interface.md

Veamos un ejemplo sencillo donde probar muchas operaciones con un array:

Escenario: tabla de datos
Dados los siguientes datos: 
| Operador1 | Operador2 | Operacion | Resultado |
| 10        | 5         | +         | 15        |
| 20        | 10        | -         | 10        |
Entonces las reglas se cumplen

Este es el código generado por la extensión:

test('tabla de datos', ({
  dados,
  entonces
}) => {
  dados('los siguientes datos:', (table) => {

  });

  entonces('las reglas se cumplen', () => {

  });
});

Vamos, antes de empezar, a volcar a la consola el parámetro table donde recibimos los datos para ver la pinta que tiene. Es un array de pares clave-valor con el nombre de las columnas.

La única gracia es que dentro del código del test en TypeScript lo declaremos como array de tipo any para poder hacer foreach let tabla: any[];

Este es el código completo.

import { defineFeature, loadFeature } from "jest-cucumber";

const feature = loadFeature("./features/calculadora.feature");

class calculadora {
  resultado: number = 0;

  suma(operando: number): void {
    this.resultado += operando;
  }
  resta(operando: number): void {
    this.resultado -= operando;
  }
}

defineFeature(feature, test => {
  test("Resta simple", ({ given, when, then }) => {
    let cal: calculadora = new calculadora();

    given(/^Un valor de partida de (.*)$/, arg0 => {
      cal.resultado = Number(arg0);
    });

    when(/^se le resta (.*)$/, arg0 => {
      cal.resultado -= Number(arg0);
    });

    then(/^el resultado debe ser (.*)$/, arg0 => {
      expect(cal.resultado).toBe(Number(arg0));
    });
  });

  test("tabla de datos", ({ given, when }) => {
    let tabla: any[];
    let cal: calculadora;

    given("los siguientes datos:", table => {
      tabla = table;
    });

    when("las reglas se cumplen", () => {
      console.log(tabla);

      tabla.forEach(element => {
        {
          cal = new calculadora();
          cal.resultado = Number(element.Operador1);

          if (element.Operacion == "+") {
            cal.suma(Number(element.Operador2));
          } else if (element.Operacion == "-") {
            cal.resta(Number(element.Operador2));
          } // para otras operaciones
          else {
          }
          expect(cal.resultado).toBe(Number(element.Resultado));
        }
      });
    });
  });
});

Y podemos ver el resultado de la ejecución.

Cambio un dato de la tabla (15 por 16) para verificar que funciona correctamente. Podemos ver cómo el test nos avisa del fallo.

Ya para terminar, vamos a configurar Jest para que genere un informe básico html con test-html-reporter.

Lo instalamos: ver https://www.npmjs.com/package/jest-html

Modificamos el fichero jest.config.js

Añadimos un reporter por defecto y al ejecutar veremos en la consola el path del informe generado en verde.

Ya tenemos la base para entender cómo se configuran reporters, por si queremos añadir alguno más específico.

Bueno, espero que este tutorial os sirva para configurar vuestro entorno y para mejorar vuestro escenario de BDD/TDD con TypeScript.

Como pasa siempre, todo es fácil una vez hecho, pero creedme que lleva un rato hasta que juntas todas las piezas dispersas 😉

DEJA UNA RESPUESTA

Por favor ingrese su comentario!

He leído y acepto la política de privacidad

Por favor ingrese su nombre aquí

Información básica acerca de la protección de datos

  • Responsable:
  • Finalidad:
  • Legitimación:
  • Destinatarios:
  • Derechos:
  • Más información: Puedes ampliar información acerca de la protección de datos en el siguiente enlace:política de privacidad