mobile menu icon

Usando Combinaciones de ApprovalsJs en WebStorm

Publicado por Fran Reyes & Manuel Rivero el 25/02/2026

Legacy Code, Approval Testing, Testing


Introducción.

En un post anterior, explicamos como integrar ApprovalsJS con WebStorm para facilitar la aplicación de la técnica Golden Master + Sampling que puede ser muy útil cuando trabajamos con código legacy[1].

ApprovalsJS nos ayuda con algunos de los pasos más complicados y/o engorrosos de la técnica Golden Master + Sampling.

Una estrategia para el sampling.

Una de las partes más tediosas, pero necesaria para poder aplicar la técnica Golden Master es obtener el input y el output necesarios para generar los datos de nuestro golden master inicial, esta obtención de datos es lo que se conoce como sampling (muestreo).

Existen diferentes estrategias para hacer el muestreo. Algunas de ellas son:

  1. Hacer combinatoria de inputs relevantes para el flujo de la aplicación y grabar el output correspondiente. Esta estrategia se puede hacer incrementalmente o con utilidades facilitadas por librerías.
  2. Generar input de forma aleatoria y grabar el output correspondiente.
  3. Grabar el input y output en una ejecución real del software para luego usarlo en los tests.

La elección de una u otra estrategia dependerá de nuestro contexto, pero lo ideal es elegir una estrategia que nos permita generar el golden master con el menor coste posible.

Si tras evaluar nuestras opciones está la posibilidad de hacer combinatoria de inputs (estrategia 1), la librería ApprovalsJS puede facilitar bastante este trabajo.

Generando combinaciones de inputs con Approvals.

ApprovalsJS proporciona la función verifyAllCombinations que genera todas las combinaciones posibles de los valores de inputs que le indiquemos.

ApprovalsJS también se encarga de grabar el output aprobado.

La aprobación del output es responsabilidad del desarrollador, aunque, como ya vimos en posts anteriores, ApprovalsJS proporciona herramientas que la facilitan.

La firma de la función recibe como primer parámetro la función que ejecutará al sistema que estamos testeando y devolverá el output que esperamos verificar. Los siguientes parámetros de entradas son opcionales, y cada uno de ellos es un array de valores correspondiente a cada parámetro de la función que ejecuta al sistema que estamos testeando.

Veamos un ejemplo de uso:

import {verifyAllCombinations} from "approvals/lib/Providers/Jest/CombinationApprovals";
describe('Using Approvals', () => {
it('verify all combinations', () => {
const names = ['Regular'];
const sellIns = [0, 1, 5];
const qualities = [20, 50, 49, 1];
verifyAllCombinations(updateStore, names, sellIns, qualities);
});
function updateStore(name: string, sellIn: number, quality: number): GildedRose {
const store = new GildedRose([new Item(name, sellIn, quality)]);
store.update();
return store;
}
});

El test que se muestra en el ejemplo generará como input para la función updateStore el producto cartesiano de los conjuntos {‘Regular’}, {0, 1, 5} y {20, 50, 49, 1}. Eso quiere decir que, cuando aceptamos el output que produce el test, habremos generado un golden master que consta de 1 x 3 x 4 = 12 ejemplos que son el resultado de combinar los conjuntos de inputs iniciales, es decir habrá un ejemplo cuyo input es 'Regular', 0, 20, otro con 'Regular', 1, 20, otro con 'Regular', 5, 20, etc.

Usar combinaciones con ApprovalsJS puede ser muy útil en aquellos casos en los que conocemos qué valores de inputs son relevantes para conseguir ejercitar diferentes flujos de ejecución.

Problemas al usar verifyAllCombinations en WebStorm.

En post anteriores, explicamos cómo al utilizar ApprovalsJS desde WebStorm nos encontramos con el problema de que no lanzaba la herramienta de diff que se usa en caso de encontrar discrepancias entre el output aprobado (golden master) y el output real que está generando la ejecución de los tests (dicha herramienta se denomina Reporter[2]). Dicho problema lo superamos configurando la herramienta para incluir en la lista de reporters, un reporter que creamos para que funcione con la herramienta de diff de WebStorm: WebStormReporter.

Este era su código:

import {verifyAsJson as verifyAsJsonWithJest} from "approvals/lib/Providers/Jest/JestApprovals";
import {Options} from "approvals/lib/Core/Options";
import {GenericDiffReporterBase} from "approvals/lib/Reporting/GenericDiffReporterBase";
import {searchForExecutable} from "approvals/lib/AUtils";
import {Config} from "approvals/lib/config";
export function verifyAsJson(data: any[]) {
verifyAsJsonWithJest(data, new Options().withReporter(new WebStormReporter()));
}
class WebStormReporter extends GenericDiffReporterBase {
constructor() {
super("webstorm-reporter");
this.exePath = searchForExecutable("webstorm");
}
public report(
approved: string,
received: string,
options: Partial<Config> = {},
): void {
options.cmdArgs = ["diff", received, approved];
return super.report(approved, received, options);
}
}

Pero esta solución no nos sirve en el caso de verifyAllCombinations ya que este método no nos permite configurar qué reporter usar pasando un parámetro, como sí que hacía la función verifyAsJson. Así que debemos buscar otra solución.

Nuestra solución.

La solución que hemos encontrado para poder configurar el reporter consiste en cambiar la configuración por defecto de ApprovalsJS antes de ejecutar la función verifyAllCombinations.

Como la configuración por defecto de ApprovalsJS tiene un ámbito global, cualquier cambio que le hagamos puede influir en la ejecución de otros tests. Para evitarlo, debemos deshacer el cambio después de ejecutar verifyAllCombinations dejando la configuración por defecto de ApprovalsJS en su estado anterior. Con este fin escribimos un wrapper para la función verifyAllCombinations. Nuestro wrapper, primero, modifica la configuración de ApprovalsJS para que use el reporter que queremos, después llama a verifyAllCombinations, y por último, deja la configuración como estaba inicialmente.

Este es el código del wrapper:

import {WebStormReporter} from "./webStormReporter";
import {configure} from "approvals/lib/Approvals";
import {
Printer,
VariationsForEachParameter,
verifyAllCombinations as approvalsVerifyAllCombinations
} from "approvals/lib/Providers/Jest/CombinationApprovals";
import {currentConfig} from "approvals/lib/config";
export function verifyAllCombinations<T extends any[]>(
func: Printer<T>,
...variations: VariationsForEachParameter<T>) {
configureWebStormReporter();
approvalsVerifyAllCombinations(func, ...variations);
resetConfiguration()
}
function configureWebStormReporter() {
const currentConfig = getCurrentConfig();
currentConfig.reporters = [
new WebStormReporter(),
...currentConfig.reporters,
];
configure(currentConfig);
}
function resetConfiguration() {
const currentConfig = getCurrentConfig();
currentConfig.reporters = currentConfig.reporters.slice(1);
configure(currentConfig);
}
function getCurrentConfig(){
return JSON.parse(JSON.stringify(currentConfig()));
}

La clase WebStormReporter se quedó como estaba.

Para utilizar el wrapper solo tenemos que importarlo en nuestros tests en vez de importar la función verifyAllCombinations de la librería. Con esto ya basta porque el wrapper tiene la misma firma que la función de la librería:

import {verifyAllCombinations} from "./helpers/approvalVerifiers";
describe('Gilded Rose', () => {
it('verify all combinations', () => {
const names = ['Regular'];
const sellIns = [0];
const qualities = [20];
verifyAllCombinations(updateStore, names, sellIns, qualities);
});
function updateStore(name: string, sellIn: number, quality: number): GildedRose {
const store = new GildedRose([new Item(name, sellIn, quality)]);
store.update();
return store;
}
});

Conclusión.

En este post abordamos un problema que se nos presentó al usar la función verifyAllCombinations de ApprovalsJS desde WebStorm en el contexto de la técnica Golden Master + Sampling.

Resulta que en WebStorm no se lanza correctamente la herramienta de diff cuando aparecen discrepancias entre el output aprobado y el que generan los tests. Esto sucede porque la interfaz de la función verifyAllCombinations no permite configurar el reporter por parámetro, a diferencia de otras funciones de la librería.

La solución práctica que planteamos consiste en crear un wrapper alrededor de verifyAllCombinations. Este wrapper cambia la configuración por defecto para forzar que se use el reporter compatible con WebStorm que presentamos en posts anteriores, WebStormReporter, ejecuta la verificación y finalmente restaura la configuración original para evitar que se vean afectados otros tests. De este modo, podemos usar verifyAllCombinations en WebStorm sin perder la integración con la su propia herramienta de diff.

Referencias.

Notas.

[1] Vemos esta y otras técnicas en profundidad en nuestro curso Cambiando Legacy.

[2] Approvals define toda una serie de abstracciones fundamentales (Writer, Reporter, Namer..) que permiten extender su comportamiento y adaptarlo en caso de que sea necesario.

Volver a posts