
GUÍA COMPLETA DE TEST-DRIVEN DEVELOPMENT EN JAVASCRIPT Y REACT
Introducción al Test-Driven Development
El desarrollo basado en pruebas, conocido como Test-Driven Development (TDD), es una práctica de programación que prioriza la creación de pruebas antes de escribir el código de la aplicación. Este enfoque fomenta la construcción de software confiable y mantenible al definir claramente los resultados esperados desde el inicio. En este tutorial, exploraremos cómo implementar TDD en aplicaciones JavaScript y React, utilizando herramientas como Jest y React Testing Library. Cubriremos desde los conceptos básicos hasta ejemplos prácticos, incluyendo pruebas unitarias, de integración y de extremo a extremo, junto con el uso de dobles de prueba.
¿Qué es el Test-Driven Development?
El Test-Driven Development es una metodología donde se escriben pruebas que especifican el comportamiento esperado de un programa antes de desarrollar su lógica. Este proceso asegura que el código cumpla con los requisitos establecidos desde el principio. El flujo de trabajo típico de TDD sigue estos pasos:
- Escribir una prueba que defina el resultado esperado.
- Ejecutar la prueba, que inicialmente fallará porque el código aún no existe.
- Desarrollar el código mínimo necesario para pasar la prueba.
- Ejecutar nuevamente la prueba para verificar que pasa.
- Refactorizar el código si es necesario, asegurando que las pruebas sigan pasando.
- Repetir el ciclo hasta completar la funcionalidad.
Por ejemplo, si deseas crear una calculadora de suma, primero escribirías una prueba que especifique que sumar 4 y 6 debe devolver 10. Luego, desarrollarías la función que realiza esta operación.
// Prueba
function additionCalculatorTester() {
if (additionCalculator(4, 6) === 10) {
console.log("✔ Prueba Exitosa");
} else {
console.error("❌ Prueba Fallida");
}
}
// Programa
function additionCalculator(a, b) {
return a + b;
}
// Ejecutar la prueba
additionCalculatorTester();
Ejemplo de Flujo de Trabajo TDD en JavaScript
A continuación, se detalla un ejemplo práctico de TDD en JavaScript para una calculadora de suma, siguiendo los pasos del flujo de trabajo.
Escribir la Prueba
Primero, se crea una prueba que especifica el comportamiento esperado. En este caso, se espera que la función additionCalculator
sume dos números correctamente.
function additionCalculatorTester() {
if (additionCalculator(4, 6) === 10) {
console.log("✔ Prueba Exitosa");
} else {
console.error("❌ Prueba Fallida");
}
}
Desarrollar el Programa
Se escribe el código necesario para pasar la prueba, implementando la función additionCalculator
.
function additionCalculator(a, b) {
return a + b;
}
Ejecutar la Prueba
Se ejecuta la prueba para verificar si el programa cumple con las expectativas.
additionCalculatorTester();
Refactorizar la Prueba
Si es necesario, se refactoriza la prueba para mejorar su legibilidad, por ejemplo, utilizando un operador condicional.
function additionCalculatorTester() {
additionCalculator(4, 6) === 10
? console.log("✔ Prueba Exitosa")
: console.error("❌ Prueba Fallida");
}
Refactorizar el Programa
El programa también puede refactorizarse, por ejemplo, convirtiendo la función en una función flecha.
const additionCalculator = (a, b) => a + b;
Reejecutar la Prueba
Se vuelve a ejecutar la prueba para confirmar que el programa sigue funcionando correctamente.
additionCalculatorTester();
Este ejemplo ilustra cómo TDD permite desarrollar código de manera incremental, asegurando que cada cambio mantenga la funcionalidad esperada.
Cómo Usar Jest para Implementar Pruebas
Jest es una herramienta de pruebas ampliamente utilizada que simplifica la implementación de TDD en JavaScript. A continuación, se explica cómo configurar y usar Jest para probar una calculadora de suma.
Configuración Inicial
-
Verificar Node y NPM: Asegúrate de tener Node.js (versión 10.16 o superior) y NPM (versión 5.6 o superior) instalados. Puedes descargarlos desde el sitio oficial de Node.js.
-
Crear un Directorio de Proyecto:
mkdir calculadora-suma-jest
cd calculadora-suma-jest
- Inicializar package.json:
npm init -y
- Instalar Jest:
npm install jest --save-dev
- Configurar Jest en package.json:
{
"scripts": {
"test": "jest"
}
}
- Crear Archivos del Proyecto:
touch additionCalculator.js
touch additionCalculator.test.js
Escribir la Prueba
En el archivo additionCalculator.test.js
, se escribe una prueba que especifica el comportamiento esperado.
const additionCalculator = require("./additionCalculator");
test("suma de 4 y 6 igual a 10", () => {
expect(additionCalculator(4, 6)).toBe(10);
});
En este código, test
define el caso de prueba, expect
verifica el resultado y toBe
compara con un valor primitivo.
Desarrollar el Programa
En additionCalculator.js
, se implementa la función para pasar la prueba.
function additionCalculator(a, b) {
return a + b;
}
module.exports = additionCalculator;
Ejecutar la Prueba
Se ejecuta la prueba con:
npm run test
Jest mostrará un mensaje indicando si la prueba pasó o falló.
Refactorizar la Prueba
Para probar más casos, como sumar múltiples números o manejar casos sin argumentos, se refactoriza la prueba.
const additionCalculator = require("./additionCalculator");
describe("Casos de prueba de additionCalculator", () => {
test("suma de 4 y 6 igual a 10", () => {
expect(additionCalculator(4, 6)).toBe(10);
});
test("suma de 100, 50, 20, 45 y 30 igual a 245", () => {
expect(additionCalculator(100, 50, 20, 45, 30)).toBe(245);
});
test("suma de 7 igual a 7", () => {
expect(additionCalculator(7)).toBe(7);
});
test("sin argumentos devuelve 0", () => {
expect(additionCalculator()).toBe(0);
});
});
La función describe
agrupa casos de prueba relacionados, mejorando la organización.
Refactorizar el Programa
Se ajusta additionCalculator
para manejar múltiples argumentos usando el operador rest y reduce
.
function additionCalculator(...numbers) {
return numbers.reduce((sum, item) => sum + item, 0);
}
module.exports = additionCalculator;
Reejecutar la Prueba
Se vuelve a ejecutar la prueba para verificar que todo funciona correctamente.
npm run test
Para automatizar las pruebas, agrega --watchAll
al script de prueba en package.json
:
{
"scripts": {
"test": "jest --watchAll"
}
}
Uso de Módulos ES6 con Jest
Jest no soporta módulos ES6 de forma nativa, pero puedes habilitarlos con Babel. Sigue estos pasos:
- Instalar Babel:
npm install @babel/preset-env --save-dev
- Crear .babelrc:
touch .babelrc
- Configurar .babelrc:
{ "presets": ["@babel/preset-env"] }
- Ajustar Archivos:
Cambia require
por import
en additionCalculator.test.js
:
import additionCalculator from "./additionCalculator";
test("suma de 4 y 6 igual a 10", () => {
expect(additionCalculator(4, 6)).toBe(10);
});
Y en additionCalculator.js
:
export default function additionCalculator(...numbers) {
return numbers.reduce((sum, item) => sum + item, 0);
}
- Reejecutar la Prueba:
npm run test
Esto permite usar la sintaxis moderna de ES6 en proyectos con Jest.
Ventajas del Test-Driven Development
El TDD ofrece múltiples beneficios que mejoran el proceso de desarrollo de software.
Claridad en el Propósito del Programa
TDD te obliga a definir claramente el comportamiento esperado antes de escribir el código, lo que ayuda a comprender mejor los requisitos del programa. Al escribir pruebas primero, documentas las expectativas, lo que facilita el desarrollo enfocado.
Mayor Confianza en el Código
Las pruebas proporcionan una red de seguridad, asegurando que el código funcione como se espera, incluso después de refactorizaciones o actualizaciones. Esto aumenta la confianza en la estabilidad del programa.
Tipos de Pruebas en TDD
TDD abarca diferentes tipos de pruebas, cada una con un propósito específico.
Pruebas Unitarias
Las pruebas unitarias evalúan unidades independientes de código, como una función que no depende de otras partes del sistema. Por ejemplo, la prueba inicial de additionCalculator
es una prueba unitaria porque verifica una función aislada.
test("suma de 4 y 6 igual a 10", () => {
expect(additionCalculator(4, 6)).toBe(10);
});
Pruebas de Integración
Las pruebas de integración verifican el funcionamiento de componentes que dependen de otros, como una función que usa métodos externos. La versión refactorizada de additionCalculator
que usa reduce
es un ejemplo, ya que depende de una API de JavaScript.
test("suma de múltiples números", () => {
expect(additionCalculator(100, 50, 20)).toBe(170);
});
Pruebas de Extremo a Extremo
Las pruebas de extremo a extremo (E2E) evalúan la interfaz de usuario completa, simulando la interacción del usuario con la aplicación. Estas pruebas verifican que todos los componentes trabajen juntos correctamente.
Dobles de Prueba
Los dobles de prueba son objetos que imitan dependencias externas, como bases de datos o APIs. Incluyen:
- Dummy: Imita valores de parámetros para pruebas.
- Mock: Simula una dependencia sin considerar sus respuestas.
- Stub: Devuelve valores predefinidos para probar diferentes escenarios.
- Fake: Implementa una versión simplificada de una dependencia, como una base de datos local.
Por ejemplo, un stub podría simular una respuesta de API:
const mockApiResponse = () => ({ data: "respuesta simulada" });
Cómo Probar Componentes de React
Probar componentes de React requiere un test runner (como Jest) y una biblioteca de pruebas de componentes (como React Testing Library). A continuación, se detalla un proyecto práctico.
Configuración del Proyecto
-
Verificar Node y NPM: Usa Node 10.16+ y NPM 5.6+.
-
Crear un Proyecto React:
npx create-react-app react-testing-project
cd react-testing-project
- Limpiar la Carpeta src:
Elimina todos los archivos en src
y crea:
touch src/index.js
touch src/App.js
touch src/App.test.js
- Configurar index.js:
import React from "react";
import { createRoot } from "react-dom/client";
import App from "./App";
createRoot(document.getElementById("root")).render(<App />);
Escribir la Prueba
En App.test.js
, se especifica que el componente App
debe renderizar un encabezado con el texto “CodeSweetly Test”.
import React from "react";
import { render, screen } from "@testing-library/react";
import "@testing-library/jest-dom";
import App from "./App";
test("encabezado codesweetly test", () => {
render(<App />);
expect(screen.getByRole("heading")).toHaveTextContent(/codesweetly test/i);
});
Desarrollar el Componente
En App.js
, se crea el componente para pasar la prueba.
import React from "react";
const App = () => <h1>CodeSweetly Test</h1>;
export default App;
Ejecutar la Prueba
npm test App.test.js
Refactorizar la Prueba
Para probar una interacción, como cambiar el texto del encabezado al hacer clic en un botón, se refactoriza App.test.js
.
import React from "react";
import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import "@testing-library/jest-dom";
import App from "./App";
describe("Componente App", () => {
test("encabezado codesweetly test", () => {
render(<App />);
expect(screen.getByRole("heading")).toHaveTextContent(
/codesweetly test/i
);
});
test("actualización de encabezado", () => {
render(<App />);
const button = screen.getByRole("button", {
name: "Actualizar Encabezado",
});
userEvent.click(button);
expect(screen.getByRole("heading")).toHaveTextContent(
/proyecto codesweetly/i
);
});
});
Refactorizar el Componente
Se ajusta App.js
para manejar el cambio de estado.
import React, { useState } from "react";
const App = () => {
const [heading, setHeading] = useState("CodeSweetly Test");
const handleClick = () => {
setHeading("Proyecto CodeSweetly");
};
return (
<>
<h1>{heading}</h1>
<button type="button" onClick={handleClick}>
Actualizar Encabezado
</button>
</>
);
};
export default App;
Reejecutar la Prueba
npm test App.test.js
Iniciar la Aplicación
npm start
Esto abrirá la aplicación en el navegador, mostrando los cambios.
Conclusiones
El Test-Driven Development es una práctica poderosa que mejora la calidad y mantenibilidad del código en aplicaciones JavaScript y React. Al escribir pruebas antes del código, los desarrolladores definen claramente los requisitos, lo que resulta en un software más robusto y confiable. Herramientas como Jest y React Testing Library facilitan la implementación de TDD, permitiendo pruebas unitarias, de integración y de extremo a extremo. Incorporar TDD en tu flujo de trabajo no solo aumenta la confianza en el código, sino que también optimiza el proceso de desarrollo, reduciendo errores y mejorando la escalabilidad de los proyectos.