
Aprender a escribir un test unitario en JavaScript puede parecer un paso muy técnico al principio, especialmente si vienes de centrarte en crear interfaces, resolver funcionalidades o entregar cambios rápido. Sin embargo, cuanto antes incorporas el testing a tu forma de trabajar, antes empiezas a notar una diferencia importante: desarrollas con más seguridad, detectas errores antes y entiendes mejor cómo debería comportarse tu propio código.
Un test unitario no es una garantía absoluta de que una aplicación nunca fallará, pero sí es una herramienta muy útil para comprobar que una parte pequeña y concreta del código hace exactamente lo que esperas. En lugar de abrir el navegador, probar manualmente varios casos y confiar en que todo sigue funcionando, puedes escribir una prueba automatizada que verifique ese comportamiento por ti.
En este artículo vamos a ver cómo escribir tu primer test unitario en JavaScript desde cero, con una explicación clara, ejemplos sencillos y buenas prácticas para que no solo copies código, sino que entiendas qué estás haciendo. Si todavía estás construyendo una base sobre testing, también puede ayudarte leer esta guía sobre qué es el testing unitario y cuándo usarlo, donde se explica el concepto desde una perspectiva más general.
Qué es un test unitario en JavaScript
Un test unitario es una prueba automatizada que verifica el comportamiento de una unidad pequeña de código. Esa unidad puede ser una función, un método, una utilidad, una transformación de datos o una pieza lógica concreta de una aplicación.
Por ejemplo, si tienes una función que suma dos números, un test unitario puede comprobar que al pasarle 2 y 3, el resultado sea 5. Parece algo demasiado simple, pero esa sencillez es precisamente una de las claves del testing unitario: comprobar piezas pequeñas de forma aislada.
function sumar(a, b) {
return a + b;
}
Un test unitario para esa función podría expresar algo como:
esperamos que sumar(2, 3) devuelva 5
La ventaja está en que esta comprobación se puede ejecutar todas las veces que quieras. Si más adelante alguien modifica la función y rompe su comportamiento, el test fallará y te avisará.
Diferencia entre probar manualmente y escribir tests
Cuando pruebas manualmente una funcionalidad, normalmente sigues una serie de pasos: abres la aplicación, haces clic, introduces datos y observas el resultado. Este proceso es necesario en muchos casos, pero también tiene límites. Puedes olvidarte de revisar un caso, repetir pruebas consume tiempo y es fácil asumir que algo funciona solo porque funcionó una vez.
En cambio, las pruebas unitarias JavaScript permiten automatizar comprobaciones pequeñas y repetibles. No sustituyen todo el trabajo de revisión, pero sí reducen la dependencia de la memoria y de la intuición.
Probar manualmente responde a la pregunta: “¿parece que esto funciona ahora?”.
Un test unitario responde a la pregunta: “¿esta parte concreta del código sigue comportándose como habíamos definido?”.
Si quieres ampliar la visión general, puedes complementar esta lectura con el artículo sobre tipos de testing en aplicaciones web, donde se explican las diferencias entre pruebas unitarias, de integración, end to end y otros enfoques.
Por qué deberías aprender testing JavaScript desde el principio
Muchas personas empiezan a interesarse por el testing JavaScript cuando el proyecto ya ha crecido, cuando hay miedo a tocar ciertas partes del código o cuando una corrección rompe otra funcionalidad sin querer. Aunque nunca es tarde para empezar, aprender testing desde el principio tiene varias ventajas.
La primera es que te ayuda a escribir código más claro. Para poder testear una función, esa función debe tener una responsabilidad concreta. Si una función hace demasiadas cosas, tiene dependencias ocultas o mezcla lógica con efectos secundarios, escribir un test se vuelve complicado. Por eso, el testing también actúa como una señal de diseño.
La segunda ventaja es que te da confianza al refactorizar. Refactorizar significa mejorar la estructura interna del código sin cambiar su comportamiento externo. Si tienes tests que verifican ese comportamiento, puedes reorganizar el código con menos miedo.
La tercera ventaja es que mejora la comunicación dentro de un equipo. Un test bien escrito funciona casi como documentación ejecutable. Explica qué se espera de una función y qué casos se consideran importantes.
Cuándo tiene sentido escribir un test unitario
No hace falta testear absolutamente todo. Un error común al empezar es pensar que cualquier línea de código debe tener una prueba asociada. En la práctica, conviene priorizar aquellas partes donde un fallo puede tener impacto real.
Tiene mucho sentido escribir tests unitarios para funciones que transforman datos, validaciones de formularios, cálculos de precios, utilidades reutilizables, reglas de negocio o casos límite que podrían romperse con facilidad.
Por ejemplo, si tienes una función que calcula el precio final de un producto con descuento, ahí un test unitario puede ahorrarte muchos problemas. Si el cálculo falla, el impacto puede ser importante.
Qué no suele ser ideal para un primer test unitario
Para empezar, no conviene elegir una parte demasiado compleja de la aplicación. Si tu primer test intenta cubrir una pantalla completa, una llamada a una API, una interacción con el DOM y varios estados al mismo tiempo, es probable que termines frustrada.
Para tu primer test unitario JavaScript, lo mejor es elegir una función pura: una función que recibe unos datos, devuelve un resultado y no depende de elementos externos. Este enfoque se relaciona directamente con la forma de trabajar explicada en la guía sobre cómo testear funciones puras en JavaScript.
Preparar el entorno para escribir tu primer test
Para escribir tests en JavaScript necesitas un entorno de ejecución y una herramienta de testing. En este artículo vamos a usar Jest, porque es una opción muy conocida, tiene una sintaxis accesible y permite empezar con pocos pasos.
También podrías usar Vitest, especialmente si trabajas con Vite, React o proyectos frontend modernos. La lógica de los primeros tests es muy parecida: defines un caso, ejecutas una función y compruebas el resultado esperado. Si tu proyecto ya usa Vite, te puede interesar esta guía específica sobre Vitest para proyectos modernos con Vite.
Crear un proyecto básico
Primero, crea una carpeta para el ejemplo:
mkdir primer-test-js
cd primer-test-js
Después inicializa un proyecto con npm:
npm init -y
Esto generará un archivo package.json, donde se guardará la configuración básica del proyecto y los scripts que podrás ejecutar desde la terminal.
Ahora instala Jest como dependencia de desarrollo:
npm install --save-dev jest
Una dependencia de desarrollo es una herramienta que necesitas para trabajar en el proyecto, pero que no forma parte necesariamente del código final que se ejecutará en producción.
Configurar el script de test
Abre el archivo package.json y busca la sección "scripts". Puedes modificarla así:
{
"scripts": {
"test": "jest"
}
}
Con esto podrás ejecutar los tests usando:
npm test
Este comando le dirá a Jest que busque archivos de prueba en el proyecto y los ejecute.
Crear una función sencilla para testear
Vamos a empezar con una función muy simple. Crea un archivo llamado sumar.js:
function sumar(a, b) {
return a + b;
}
module.exports = sumar;
Esta función recibe dos valores y devuelve la suma. Además, usamos module.exports para poder importarla desde el archivo de test.
Aunque el ejemplo sea básico, tiene algo importante: la función es predecible. Si le pasamos 2 y 3, esperamos 5. Si le pasamos -1 y 1, esperamos 0. Esta claridad facilita mucho el aprendizaje.
Qué significa testear una función
Testear una función no consiste solo en comprobar que “funciona”. Eso es demasiado general. Un buen test define un escenario concreto.
Por ejemplo:
- Cuando sumo
2 + 3, el resultado debe ser5. - Cuando sumo
-1 + 1, el resultado debe ser0. - Cuando sumo
0 + 0, el resultado debe ser0.
Cada caso expresa una expectativa. Esa expectativa es el corazón de cualquier prueba.
Escribir tu primer test unitario en JavaScript
Ahora crea un archivo llamado sumar.test.js. Jest reconoce por defecto archivos que contienen .test.js o .spec.js, así que este nombre es una convención habitual.
Dentro del archivo escribe:
const sumar = require('./sumar');
test('suma dos números positivos correctamente', () => {
expect(sumar(2, 3)).toBe(5);
});
Este es tu primer test unitario en JavaScript.
Vamos a descomponerlo.
La función test
La función test define un caso de prueba. Recibe dos argumentos principales:
test('descripción del comportamiento', () => {
// comprobación
});
El primer argumento es una descripción. Conviene que sea clara y específica. No es lo mismo escribir:
test('funciona', () => {})
que escribir:
test('suma dos números positivos correctamente', () => {})
La segunda opción comunica mucho mejor qué comportamiento se está verificando.
La función expect
La función expect sirve para expresar el resultado que esperas obtener. En este caso:
expect(sumar(2, 3)).toBe(5);
Esto significa: “espero que el resultado de ejecutar sumar(2, 3) sea 5”.
toBe es un matcher, es decir, una función que compara el resultado recibido con el valor esperado. Para valores primitivos como números, strings o booleanos, toBe suele ser suficiente.
Ejecutar el test
Ahora ejecuta en la terminal:
npm test
Si todo está bien, Jest mostrará un mensaje indicando que el test ha pasado. Eso significa que la función se comporta como esperabas en ese caso concreto.
Añadir más casos de prueba
Un solo test está bien para empezar, pero normalmente una función debe comprobarse con varios escenarios. Vamos a ampliar el archivo sumar.test.js:
const sumar = require('./sumar');
test('suma dos números positivos correctamente', () => {
expect(sumar(2, 3)).toBe(5);
});
test('suma un número negativo y uno positivo correctamente', () => {
expect(sumar(-1, 1)).toBe(0);
});
test('suma dos ceros correctamente', () => {
expect(sumar(0, 0)).toBe(0);
});
Ahora tienes tres pruebas. Cada una cubre un caso diferente.
Esto es importante porque una función puede funcionar en un caso y fallar en otro. Por eso, escribir pruebas unitarias JavaScript no va solo de confirmar el caso feliz, sino también de pensar en límites, variantes y posibles errores.
El patrón Arrange, Act, Assert
Una forma muy útil de estructurar tests es el patrón Arrange, Act, Assert:
- Arrange: preparas los datos necesarios.
- Act: ejecutas la función que quieres probar.
- Assert: compruebas el resultado esperado.
El test anterior podría escribirse así:
test('suma dos números positivos correctamente', () => {
// Arrange
const a = 2;
const b = 3;
// Act
const resultado = sumar(a, b);
// Assert
expect(resultado).toBe(5);
});
Para una función tan simple puede parecer demasiado largo, pero este patrón se vuelve muy útil cuando los tests crecen. Ayuda a mantener el orden y facilita la lectura.
Cuándo usar una versión más compacta
Si el test es evidente, una versión compacta está bien:
expect(sumar(2, 3)).toBe(5);
Pero si hay varios datos, condiciones o pasos intermedios, separar preparación, acción y comprobación mejora mucho la mantenibilidad.
Probar una función un poco más realista
La suma es útil para entender la mecánica, pero vamos a ver un ejemplo algo más cercano a un proyecto real. Imagina que tienes una función para aplicar un descuento a un precio.
Crea un archivo calcularPrecioFinal.js:
function calcularPrecioFinal(precio, descuento) {
if (descuento < 0 || descuento > 100) {
throw new Error('El descuento debe estar entre 0 y 100');
}
return precio - precio * (descuento / 100);
}
module.exports = calcularPrecioFinal;
Esta función recibe un precio y un porcentaje de descuento. Si el descuento no está entre 0 y 100, lanza un error.
Ahora crea el archivo calcularPrecioFinal.test.js:
const calcularPrecioFinal = require('./calcularPrecioFinal');
test('aplica un descuento del 20% correctamente', () => {
expect(calcularPrecioFinal(100, 20)).toBe(80);
});
test('devuelve el mismo precio cuando el descuento es 0', () => {
expect(calcularPrecioFinal(100, 0)).toBe(100);
});
test('devuelve 0 cuando el descuento es 100', () => {
expect(calcularPrecioFinal(100, 100)).toBe(0);
});
test('lanza un error si el descuento es negativo', () => {
expect(() => calcularPrecioFinal(100, -10)).toThrow('El descuento debe estar entre 0 y 100');
});
Este ejemplo ya muestra algo más interesante. No solo comprobamos resultados correctos, sino también un caso de error. Eso es esencial en el testing: no basta con probar lo que debería salir bien; también hay que comprobar cómo responde el código cuando recibe valores inválidos.
Por qué conviene probar los casos límite
Los casos límite son escenarios que se encuentran en los extremos de una regla. En el ejemplo anterior, los límites son 0 y 100, porque son los valores mínimo y máximo permitidos para el descuento.
Probar estos casos ayuda a detectar errores típicos como permitir valores que deberían rechazarse, rechazar valores válidos, calcular mal porcentajes o no controlar entradas inesperadas.
En aplicaciones reales, estos detalles pueden afectar a formularios, precios, permisos, fechas, filtros o validaciones importantes. Si estás planificando tests para una aplicación completa, puede resultarte útil el artículo sobre cómo crear una estrategia de testing para una aplicación web.
Buenas prácticas para escribir tu primer test unitario
Escribir tests no consiste en acumular archivos de prueba sin criterio. Un test útil debe ser claro, estable y fácil de mantener. De lo contrario, puede convertirse en una carga.
Escribe tests con nombres descriptivos
El nombre del test debe explicar el comportamiento esperado. Una buena descripción permite entender qué ha fallado sin tener que leer todo el código.
Mejor esto:
test('lanza un error si el descuento es mayor que 100', () => {
expect(() => calcularPrecioFinal(100, 120)).toThrow();
});
Que esto:
test('error descuento', () => {
expect(() => calcularPrecioFinal(100, 120)).toThrow();
});
El primer test comunica intención. El segundo obliga a interpretar.
Prueba comportamientos, no implementaciones
Un error frecuente es escribir tests demasiado pegados a cómo está construido el código internamente. Lo importante es comprobar qué hace la función, no necesariamente cómo lo hace.
Si una función devuelve el precio final correcto, el test no debería depender de si internamente usas una variable llamada total, una operación en una línea o varios pasos separados.
Esto permite refactorizar sin romper tests innecesariamente.
Mantén cada test enfocado
Un test debería comprobar una idea principal. Si en un mismo test verificas demasiadas cosas, cuando falle será más difícil saber cuál es el problema.
Por ejemplo, en lugar de escribir un único test enorme para todos los descuentos posibles, suele ser mejor crear varios tests pequeños con nombres claros.
Evita tests frágiles
Un test frágil es aquel que falla con facilidad aunque el comportamiento importante no se haya roto. Esto suele ocurrir cuando el test depende de detalles irrelevantes, del orden exacto de datos que no importa o de configuraciones externas innecesarias.
Para evitarlo, céntrate en la salida observable de la función y reduce las dependencias externas. Este punto conecta con muchas de las recomendaciones explicadas en buenas prácticas para escribir tests mantenibles.
Errores comunes al empezar con pruebas unitarias JavaScript
Cuando empiezas con pruebas unitarias JavaScript, es normal cometer algunos errores. Lo importante es identificarlos pronto para no convertirlos en hábitos.
Testear demasiado tarde
Si esperas a que todo el proyecto esté terminado para escribir tests, probablemente te resulte más difícil. El código puede estar muy acoplado, las funciones pueden tener demasiadas responsabilidades y quizá ni siquiera tengas claro qué comportamiento esperas en algunos casos.
No hace falta aplicar TDD desde el primer día, pero sí conviene escribir tests mientras desarrollas o justo después de terminar una pieza lógica.
Confundir test unitario con test de integración
Un test unitario prueba una unidad pequeña de código de forma aislada. Un test de integración comprueba cómo colaboran varias partes entre sí.
Por ejemplo, probar una función que calcula un descuento es unitario. Probar que un formulario recoge datos, los valida, llama a una API y actualiza la interfaz se acerca más a una prueba de integración o incluso a una prueba end to end, según el caso.
Ambos tipos de test son útiles, pero tienen objetivos diferentes.
Escribir tests que no aportan valor
No todos los tests son igual de útiles. Por ejemplo, testear que una función mockeada devuelve exactamente lo que tú mismo le has dicho que devuelva no aporta demasiado. Tampoco suele tener sentido testear detalles triviales que no contienen lógica real.
Un buen criterio es preguntarte: si este test falla, ¿me estaría avisando de un problema importante?
Si la respuesta es no, quizá ese test no sea prioritario.
Para profundizar en estos fallos habituales, puedes revisar también el artículo sobre errores comunes al empezar con testing en desarrollo frontend.
Cómo saber si tu primer test está bien escrito
Un buen primer test no tiene que ser perfecto. Tiene que ayudarte a entender la dinámica básica: preparar un escenario, ejecutar código y comprobar un resultado.
Aun así, puedes revisar algunos puntos:
- El test tiene un nombre claro.
- Comprueba un comportamiento concreto.
- No depende de otros tests.
- Se puede ejecutar varias veces con el mismo resultado.
- Falla si rompes la función a propósito.
- Es fácil de leer sin demasiada explicación adicional.
Una técnica muy sencilla es modificar temporalmente la función para que devuelva un resultado incorrecto. Por ejemplo:
function sumar(a, b) {
return a - b;
}
Si el test falla, buena señal: significa que está comprobando algo real. Después, vuelve a dejar la función correcta.
Qué hacer cuando un test falla
Cuando un test falla, no lo veas como una molestia. Un test fallido es información. Te está diciendo que hay una diferencia entre el comportamiento esperado y el comportamiento actual.
Ante un fallo, revisa si el test está bien planteado, si el resultado esperado es correcto, si la función tiene un error o si ha cambiado un requisito y debes actualizar la prueba.
No todos los fallos significan que el código productivo está mal. A veces el test está desactualizado o mal definido. Lo importante es analizar el motivo antes de cambiar cosas a ciegas.
Jest, Vitest y otras herramientas de testing JavaScript
Para escribir un test unitario JavaScript necesitas una herramienta que ejecute las pruebas y te permita expresar expectativas. Jest es una opción muy popular y adecuada para empezar. Vitest, por su parte, se ha convertido en una alternativa muy cómoda en proyectos basados en Vite.
La buena noticia es que, para los primeros pasos, la sintaxis suele ser muy parecida:
test('describe un comportamiento', () => {
expect(resultado).toBe(valorEsperado);
});
Esto significa que lo más importante no es memorizar una herramienta concreta, sino entender la mentalidad del testing: definir comportamientos esperados y comprobarlos de forma automática.
Cuál elegir para empezar
Si estás aprendiendo desde cero con JavaScript puro, Jest es una opción sencilla para practicar. Si ya trabajas en un proyecto con Vite, React o una configuración moderna de frontend, Vitest puede integrarse de forma más natural.
En cualquier caso, no te obsesiones con elegir la herramienta perfecta. Para tu primer test, lo importante es empezar con una función pequeña, escribir una prueba clara y ejecutarla desde la terminal.
Ejemplo completo de primer test unitario
Para recapitular, esta sería una estructura mínima de proyecto:
primer-test-js/
├── calcularPrecioFinal.js
├── calcularPrecioFinal.test.js
├── package.json
└── node_modules/
Archivo calcularPrecioFinal.js:
function calcularPrecioFinal(precio, descuento) {
if (descuento < 0 || descuento > 100) {
throw new Error('El descuento debe estar entre 0 y 100');
}
return precio - precio * (descuento / 100);
}
module.exports = calcularPrecioFinal;
Archivo calcularPrecioFinal.test.js:
const calcularPrecioFinal = require('./calcularPrecioFinal');
test('aplica un descuento del 20% correctamente', () => {
expect(calcularPrecioFinal(100, 20)).toBe(80);
});
test('devuelve el mismo precio cuando el descuento es 0', () => {
expect(calcularPrecioFinal(100, 0)).toBe(100);
});
test('lanza un error si el descuento es menor que 0', () => {
expect(() => calcularPrecioFinal(100, -5)).toThrow('El descuento debe estar entre 0 y 100');
});
Comando para ejecutar:
npm test
Con esto ya tienes un ejemplo completo de testing JavaScript aplicado a una función sencilla, realista y fácil de entender.
Preguntas frecuentes sobre test unitario JavaScript
¿Qué debería testear primero en JavaScript?
Lo más recomendable es empezar por funciones puras y pequeñas. Por ejemplo, utilidades de formato, cálculos, validaciones o transformaciones de datos. Estas piezas son ideales para aprender porque reciben una entrada clara y devuelven una salida fácil de comprobar.
Evita empezar por componentes complejos, llamadas a APIs o lógica muy acoplada a la interfaz. Ya llegarás a eso más adelante.
¿Necesito saber mucho JavaScript para escribir tests unitarios?
No necesitas ser experta, pero sí conviene entender funciones, módulos, valores de retorno y errores básicos. Si sabes crear una función, exportarla, importarla y ejecutarla, ya tienes una buena base para escribir tu primer test unitario.
De hecho, escribir tests puede ayudarte a mejorar tu JavaScript porque te obliga a pensar con más precisión en entradas, salidas y comportamientos esperados.
¿Jest o Vitest: cuál es mejor para empezar?
Ambas herramientas son buenas. Jest es una opción muy extendida y fácil de encontrar en tutoriales, proyectos y documentación. Vitest suele encajar muy bien en proyectos modernos con Vite y ofrece una experiencia rápida e integrada.
Para aprender el concepto de pruebas unitarias JavaScript, cualquiera de las dos te sirve. Lo importante es no quedarte bloqueada en la elección de la herramienta y empezar con un caso sencillo.
Más allá del primer test: cómo seguir practicando
Una vez que escribes tu primer test, el siguiente paso es practicar con funciones un poco más variadas. Puedes crear pequeñas utilidades y probarlas.
Algunas ideas:
- Una función que valide si un email tiene un formato básico correcto.
- Una función que convierta minutos en horas y minutos.
- Una función que calcule el total de un carrito.
- Una función que filtre productos por categoría.
- Una función que formatee una fecha.
- Una función que determine si una contraseña cumple ciertos requisitos.
Lo ideal es que cada ejercicio tenga varios casos: uno normal, uno límite y uno inválido. Así empiezas a construir una mentalidad de testing más completa.
Por ejemplo, si validas una contraseña, no pruebes solo una contraseña correcta. Prueba también una demasiado corta, una sin números, una vacía y una con caracteres válidos.
Para organizar estos contenidos dentro de una visión más amplia, puedes leer la guía sobre la pirámide de testing y cómo organizar las pruebas de una aplicación.
Del “creo que funciona” al “sé que está probado”
Escribir tu primer test unitario en JavaScript no va solo de instalar Jest, crear un archivo .test.js y ejecutar npm test. Eso es la parte técnica. La parte importante es el cambio de mentalidad.
Cuando escribes tests, empiezas a definir con más claridad qué esperas de tu código. Dejas de confiar únicamente en la intuición o en pruebas manuales repetidas y empiezas a construir una red de seguridad. Esa red no elimina todos los errores, pero sí te ayuda a detectarlos antes y a trabajar con más confianza.
Al principio puede parecer lento. Es normal. Estás aprendiendo una forma nueva de pensar. Pero con la práctica, los tests se vuelven una parte natural del desarrollo. Te ayudan a refactorizar, a documentar comportamientos y a evitar que errores antiguos vuelvan a aparecer.
No necesitas empezar con una suite de testing perfecta. Empieza con una función pequeña. Escribe un test claro. Ejecútalo. Rómpelo a propósito. Arréglalo. Repite el proceso.
Ese primer test puede parecer simple, pero marca un antes y un después: pasas de preguntarte “creo que funciona” a poder decir “tengo una prueba que lo comprueba”.

