Saltar al contenido
Development

El módulo de consentimiento se construyó para ser comprobable, solo que nunca se probó

Por Victor Da Luz
testingprivacytypescriptdev-logastro-tools

Mi paquete de analítica con consentimiento primero tiene un módulo pequeño, consent.ts, que lee y escribe la decisión de rastreo de un visitante en localStorage y revisa Global Privacy Control. El comentario en la parte superior del archivo ha dicho lo mismo desde que lo escribí: storage y navigator se pasan como parámetros en vez de tocarse directamente, específicamente para que este módulo sea comprobable fuera de un navegador.

Ese comentario estuvo ahí durante meses mientras el módulo tenía cero pruebas. Finalmente las escribí, justo después de hacer el mismo ejercicio en un paquete hermano. Trece pruebas, tres funciones.

Lo que realmente necesitaba cobertura

readConsent tiene más modos de falla de los que sugieren sus cinco líneas. Nada guardado. JSON corrupto. Un número de versión que no coincide, el cual incremento cada vez que cambia lo que se rastrea para que las decisiones viejas vuelvan a preguntarse correctamente. El campo de versión ausente por completo. Un valor de decisión que no es "granted" ni "denied". Y una decisión que es explícitamente null en vez de estar ausente.

Cada uno de esos casos tiene que devolver null en silencio, sin que ninguna excepción llegue a quien llama la función. También hice que la propia lectura de storage arrojara un error, para ejercitar el try/catch que envuelve toda la función, los modos de navegación privada y algunas situaciones de cuota hacen exactamente eso.

writeConsent recibió una versión de prueba que registra cada llamada que hace, usando un falso diminuto en vez de un objeto Storage real, para poder verificar la clave exacta y el payload JSON exacto que guarda. Se compara después de volver a parsearlo como objeto, en lugar de compararlo como cadena cruda, ya que la igualdad de cadenas sobre una salida JSON fija un orden de claves que nunca se prometió. Además, una prueba de storage que arroja error para el lado de la escritura, ya que esa función absorbe el error deliberadamente: la decisión de un visitante con el storage lleno igual se aplica para la página actual, solo que se le vuelve a preguntar en la próxima visita.

La prueba que habría detectado un error real

Cada prueba hasta ese punto ejercitaba una función contra un falso que controlaba directamente. Ninguna demostraba que las dos funciones realmente concuerdan entre sí, que lo que writeConsent pone en storage es exactamente lo que readConsent espera encontrar.

Así que agregué una más: escribir una decisión, leerla de vuelta desde el mismo storage falso, comprobar que sobrevivió el viaje de ida y vuelta. Es la única prueba que detecta un desajuste de clave o un desacuerdo en el campo de versión entre las dos funciones, y también es la prueba más corta del archivo. Probar lo correcto suele costar menos que probar exhaustivamente alrededor de eso.

Lo que dejé fuera a propósito

El módulo que arranca el aviso y la barrera reales en el navegador, manipulación del DOM, escuchadores de eventos, la interfaz visible, no se tocó. Eso necesita jsdom o un navegador real para probarse con sentido, y la cobertura de funciones puras era la ganancia fácil que ya estaba ahí. Dejarlo explícitamente fuera de alcance, en vez de permitir que se colara, mantuvo esto como un cambio de un solo día en lugar de varios.

Lección

Un comentario que explica por qué un módulo está diseñado para ser comprobable no es lo mismo que el módulo esté realmente probado. Había leído ese comentario muchas veces mientras trabajaba en el archivo y nunca lo traté como un pendiente. Hizo falta hacer el mismo ejercicio en otro paquete para que las ganas se convirtieran en pruebas reales aquí.

Lecturas relacionadas