Las funciones puras son el caso perfecto para las pruebas unitarias. Para una entrada dada, siempre esperamos la misma salida: no hay ningún estado interno involucrado. Echemos un vistazo a algunos ejemplos y algunas pruebas sencillas que comprueban si los métodos funcionan como se esperaba.
Jasmine es un marco de prueba unitaria para JavaScript. Puede ejecutar pruebas tanto en Node.js como en el navegador. Se utiliza en el marco Angular y es especialmente popular en proyectos basados en Angular. Es una opción sólida para proyectos Vanilla JS o también para proyectos basados en otros marcos.
La prueba de ruta feliz es cuando probamos un método con entradas que se espera que funcione normalmente. Los argumentos son válidos y están dentro de rangos razonables. Esas pruebas verifican si el método hace su trabajo correctamente; los casos de prueba deben ser ejemplos sencillos de cómo se explica el método en su documentación.
Ejemplos de pseudocódigo:
expect(add(2, 2)).toBe(4)
,
expect(concatenate(“Lorem”, “Ipsum”)).toBe(“LoremIpsum”)
Esas pruebas están destinadas a detectarlo automáticamente cada vez que se rompe el comportamiento de la clave del método.
Veamos algunos métodos simples: operaciones simples que podríamos necesitar en algunas aplicaciones del mundo real.
Todas las implementaciones están muy simplificadas: todos los métodos fallarán de manera fea si les proporcionamos parámetros que difieran ligeramente de lo esperado. El código está lejos de ser robusto.
Método que saluda al usuario con su nombre y apellido:
export function greet(name, surname) { return `Hello ${name} ${surname}!`; }
shortDate
es un método de formato que toma un objeto de fecha y lo devuelve formateado como una cadena corta. El código:
export function shortDate(date) { return date.toISOString().substring(0, 10); }
ellipsis
toman una cadena de texto larga y un parámetro de longitud opcional, y luego recortan la cadena para que se ajuste al límite:
export function ellipsis(text, length = 50) { if (text.length > length) { return text.substring(0, length) + "…"; } return text; }
Un método que proporciona valores de cadena traducidos para un par key
y lang
. Es una implementación simplificada de lo que podría reemplazarse con bibliotecas de traducción más avanzadas.
export function translate(key, lang = "en") { switch (lang) { case "en": switch (key) { case "hello": return "Hello!"; } case "pl": switch (key) { case "hello": return "Cześć!"; } } }
Método para aplicar un porcentaje de descuento a un precio. Puede parecer excesivo con esta implementación ingenua, pero más adelante, cuando comencemos a investigar casos extremos, se volverá mucho más interesante.
export function applyDiscount(price, discountPercentage) { return price - (price * discountPercentage) / 100; }
Éste calcula el precio total al comprar varias unidades a un precio determinado. También se volverá más complicado después de agregar casos extremos interesantes.
export function calculatePrice(unitPrice, quantity) { return unitPrice * quantity; }
El código JS completo, src/main.js
:
export function greet(name, surname) { return `Hello ${name} ${surname}!`; } export function shortDate(date) { return date.toISOString().substring(0, 10); } export function ellipsis(text, length = 50) { if (text.length > length) { return text.substring(0, length) + "…"; } return text; } export function translate(key, lang = "en") { switch (lang) { case "en": switch (key) { case "hello": return "Hello!"; } case "pl": switch (key) { case "hello": return "Cześć!"; } } } export function applyDiscount(price, discountPercentage) { return price - (price * discountPercentage) / 100; } export function calculatePrice(unitPrice, quantity) { return unitPrice * quantity; }
Para agregar Jasmine, comencemos convirtiendo la carpeta en un paquete npm:
$ npm init -y Wrote to …/package.json: …
Luego podemos instalar el paquete Jasmine:
$ npm install --save-dev jasmine added 42 packages, and audited 43 packages in 2s 13 packages are looking for funding run `npm fund` for details found 0 vulnerabilities
Luego podemos generar carpetas y archivos utilizados por Jasmine:
$ npx jasmine init (no output)
Este comando genera lo siguiente:
spec/
: una carpeta donde podemos colocar archivos *.spec.js
con la prueba, y
spec/support/jasmine.json
: un archivo con la configuración de Jasmine.Para las siguientes pruebas unitarias, me centraré únicamente en el camino feliz: compruebo si el resultado es el esperado para entradas razonables. La prueba debería explicarse por sí misma, así que echémosle un vistazo:
import { greet, shortDate, ellipsis, translate, applyDiscount, calculatePrice, } from "../src/main.js"; describe("main", () => { describe("greet", () => { it("should greet by name and surname", () => { expect(greet("Lorem", "Ipsum")).toEqual("Hello Lorem Ipsum!"); }); }); describe("shortDate", () => { it("should format correclty date", () => { const date = new Date("2023-11-02"); expect(shortDate(date)).toEqual("2023-11-02"); }); }); describe("shortDate", () => { it("should shorten long text at 50 chars", () => { expect( ellipsis( "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque a faucibus massa." ) ).toEqual("Lorem ipsum dolor sit amet, consectetur adipiscing…"); }); it("should leave short text unchanged", () => { expect(ellipsis("Lorem ipsum sin dolor")).toEqual( "Lorem ipsum sin dolor" ); }); it("should shorten to custom length", () => { expect(ellipsis("Lorem ipsum sin dolor", 10)).toEqual("Lorem ipsu…"); }); }); describe("translate", () => { it("should translate to supported langauges", () => { expect(translate("hello", "en")).toEqual("Hello!"); expect(translate("hello", "pl")).toEqual("Cześć!"); }); }); describe("applyDiscount", () => { it("should lower the price accordingly", () => { expect(applyDiscount(120, 25)).toEqual(90); expect(applyDiscount(8, 50)).toEqual(4); }); }); describe("calculatePrice", () => { it("should find a price of many products", () => { expect(calculatePrice(4, 3)).toEqual(12); expect(calculatePrice(9, 0.5)).toEqual(4.5); }); }); });
( spec/main.spec.js
)
Para ejecutar las pruebas, podemos agregar el siguiente script a package.json
:
.. "scripts": { "test": "jasmine" }, …
Con esto implementado, npm run test
ejecuta nuestras pruebas:
$ npm run test > [email protected] test > jasmine Randomized with seed 76873 Started ........ 8 specs, 0 failures Finished in 0.004 seconds Randomized with seed 76873 (jasmine --random=true --seed=76873)
En esta publicación, analizamos un ejemplo simple de código JS y cómo se puede cubrir mediante pruebas unitarias. Puede encontrar el ejemplo de código completo en GitHub .
También publicado aquí