Testing Fundamental Prácticas y Ejemplos de Código
🧪 Testing Fundamental: Prácticas y Ejemplos de Código
Guía completa de prácticas y ejemplos de testing sobre conceptos fundamentales, orientada a desarrolladores que buscan aprender, reforzar y aplicar pruebas de software correctamente.
🧩 1. Conceptos Fundamentales
📘 Definición
El testing de software es la práctica de verificar y validar que un sistema cumple sus requisitos funcionales y no funcionales, detectando errores antes de llegar a producción.
🧠 Objetivos Clave
- Asegurar calidad y confiabilidad.
- Detectar errores tempranamente.
- Reducir costos y riesgos asociados al desarrollo.
- Mejorar mantenibilidad y confianza del software.
⚙️ 2. Patrones de Pruebas
AAA Pattern (Arrange – Act – Assert)
Estructura clásica para escribir tests claros:
// JavaScript (Jest)
describe('Calculator', () => {
it('should add numbers correctly', () => {
// Arrange
const a = 2, b = 3;
// Act
const result = a + b;
// Assert
expect(result).toBe(5);
});
});
`
✅ Prácticas
- Separar claramente preparación, ejecución y validación.
- Mantener tests simples y legibles.
- Nombrar las pruebas de forma natural y descriptiva.
Test Setup / Teardown
Preparar y limpiar el entorno antes/después de cada test.
// TypeScript + Jest
beforeEach(() => {
// inicializar datos
});
afterEach(() => {
// limpiar estado global
});
🧪 3. Unit Testing (Pruebas Unitarias)
📘 Concepto
Evalúa funciones o métodos individuales de manera aislada.
💻 Ejemplo (Python + pytest)
def add(a, b):
return a + b
def test_add():
assert add(2, 3) == 5
💡 Buenas Prácticas
- Evitar dependencias externas.
- Mantener tests rápidos (<1s por función).
- Probar casos normales y límites (border cases).
- Usar mocks o stubs para dependencias.
🔗 4. Integration Testing (Pruebas de Integración)
📘 Concepto
Verifica que varios componentes funcionen correctamente juntos.
💻 Ejemplo (Node.js + Supertest)
import request from 'supertest';
import app from '../app.js';
describe('POST /login', () => {
it('retorna token válido', async () => {
const res = await request(app)
.post('/login')
.send({ email: 'user@test.com', password: '1234' });
expect(res.statusCode).toBe(200);
expect(res.body.token).toBeDefined();
});
});
✅ Tips
- Usar bases de datos temporales o contenedores.
- Limpiar datos tras cada prueba.
- Simular servicios externos si es necesario.
🌐 5. Functional & API Testing
📘 Concepto
Validar funcionalidad completa o endpoints REST/API.
💻 Ejemplo (REST Assured – Java)
import io.restassured.RestAssured;
import org.junit.jupiter.api.Test;
import static io.restassured.RestAssured.*;
import static org.hamcrest.Matchers.*;
public class LoginAPITest {
@Test
void testLoginEndpoint() {
RestAssured.baseURI = "https://api.example.com";
given()
.contentType("application/json")
.body("{\"email\":\"user@test.com\",\"password\":\"1234\"}")
.when()
.post("/login")
.then()
.statusCode(200)
.body("token", notNullValue());
}
}
✅ Prácticas
- Automatizar tests para CI/CD.
- Probar validaciones de datos, errores y límites.
- Documentar respuestas esperadas.
🧠 6. Test Driven Development (TDD)
💡 Flujo Clásico
- Red: escribir un test que falle.
- Green: escribir código mínimo que pase el test.
- Refactor: mejorar código sin romper el test.
💻 Ejemplo (JavaScript + Jest)
function sum(a, b) {
return a + b;
}
test('sum adds two numbers', () => {
expect(sum(2, 3)).toBe(5);
});
✅ Prácticas
- Mantener tests pequeños y claros.
- Refactorizar con confianza.
- Escribir tests antes del código funcional.
🧩 7. Behavior Driven Development (BDD)
📘 Concepto
Se centra en comportamiento esperado usando lenguaje natural.
💻 Ejemplo (Cucumber + Gherkin)
Feature: Login
Scenario: Usuario correcto inicia sesión
Given el usuario existe con email "test@test.com" y password "1234"
When envía POST a "/login"
Then recibe un código 200 y token JWT
✅ Prácticas
- Colaborar con QA y Product.
- Usar escenarios claros y comprensibles.
- Integrar con automatización (Cucumber, SpecFlow).
🧪 8. End-to-End Testing (E2E)
📘 Concepto
Simula flujo completo de usuario desde interfaz hasta base de datos.
💻 Ejemplo (Playwright)
import { test, expect } from '@playwright/test';
test('login flow', async ({ page }) => {
await page.goto('https://app.example.com');
await page.fill('#email', 'user@test.com');
await page.fill('#password', '1234');
await page.click('button[type="submit"]');
await expect(page.locator('h1')).toHaveText('Bienvenido');
});
✅ Prácticas
- Evitar tests frágiles (flaky tests).
- Usar entornos de staging o mockeados.
- Ejecutar en pipelines CI/CD.
⚙️ 9. Mocking y Stubbing
📘 Concepto
Simular dependencias externas para aislar pruebas.
💻 Ejemplo (Jest)
import axios from 'axios';
import { fetchUser } from './user';
jest.mock('axios');
test('fetchUser returns mocked user', async () => {
axios.get.mockResolvedValue({ data: { name: 'Eduardo' } });
const user = await fetchUser(1);
expect(user.name).toBe('Eduardo');
});
✅ Tipos
- Mock: Comportamiento dinámico.
- Stub: Respuesta fija.
- Fake: Implementación simplificada.
- Spy: Verifica invocaciones y parámetros.
📊 10. Coverage & Quality Metrics
💻 Ejemplo (Jest coverage)
npx jest --coverage
📈 Métricas
- Statements: líneas ejecutadas.
- Branches: decisiones lógicas cubiertas.
- Functions: funciones ejecutadas.
- Lines: cobertura total.
✅ Herramientas
- Jest / Istanbul
- Codecov / Coveralls
- SonarQube
🧩 11. Smoke, Sanity y Regression Tests
📘 Concepto
- Smoke: verificación rápida de funcionalidades críticas.
- Sanity: validaciones básicas tras cambios menores.
- Regression: asegura que cambios no rompan funcionalidades existentes.
💻 Ejemplo (JS)
test('smoke test home page', async () => {
const response = await fetch('https://app.example.com');
expect(response.status).toBe(200);
});
✅ Prácticas
- Ejecutar antes de despliegues.
- Integrar en pipelines automáticos.
🔥 12. Pruebas de Rendimiento
💻 Ejemplo (k6)
import http from 'k6/http';
import { sleep, check } from 'k6';
export default function () {
const res = http.get('https://api.example.com/products');
check(res, { 'status is 200': (r) => r.status === 200 });
sleep(1);
}
✅ Objetivos
- Medir latencia, throughput y cuellos de botella.
- Simular carga real de usuarios.
- Integrar en CI/CD para monitorización continua.
📌 Conclusión
El testing fundamental cubre unit, integration, functional, E2E, TDD, BDD y mocks, proporcionando:
- Estructura clara y patrones reproducibles.
- Automatización inicial para pipelines.
- Base sólida para ampliar hacia prácticas avanzadas y emergentes (ML, DevSecOps, UX).
Este documento es una guía extensiva de conceptos fundamentales que todo desarrollador debe dominar antes de avanzar hacia testing avanzado o especializado.
¿Te gusta este contenido? Suscríbete vía RSS