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 :
- Primero pasos con TypeScript con Microsoft Visual Studio Code
- Integración de GitHub / Git / GibHub Desktop y MS Visual Studio Code
- Instalación de Jest con TypeScript y MS Visual Studio Code
- Instalación de Prettier en MS Visual Studio Code
- Introducción a Parcel
- Verificación de formato de código con Eslint
- Configurando Github Actions junto con TypeScript, Jest, Parcel, Eslint y Prettier
- Desplegando nuestra aplicación TypeScript con GitHub Pages
- Iniciando un juego en TypeScript
- Conexión HTTP cliente-servidor con TypeScript
-
Cursores múltiples y otras opciones y extensiones sencillas y útiles en Visual Studio Code
- El juego de la Vida en TypeScript con TDD y medida de cobertura en Jest
También me gustaría que vieras este video de 2 minutos para entender dónde estamos y a dónde vamos
En lo que pretendo estar entretenido las próximas semanas para jugar con #TypeScript. Recomendaciones de @cesalberca Me ha dicho que, cuando haya cacharreado, en un rato lo montamos todo ?? pic.twitter.com/gdfBRFMQiT
— Roberto Canales Mora (@rcanalesmora) January 8, 2020
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://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 😉