tutoriales.com

Pruebas Unitarias Avanzadas en Angular: Dominando Jasmine, Karma y Jest para Aplicaciones Robusta

Este tutorial completo te guía a través de las técnicas más avanzadas para realizar pruebas unitarias en Angular. Descubre cómo configurar tu entorno, aislar componentes con mocks y spies, probar código asíncrono con RxJS y migrar a Jest para optimizar tus pruebas.

Avanzado12 min de lectura13 views
Reportar error

Introducción a las Pruebas Unitarias en Angular Intermedio

Las pruebas unitarias son un pilar fundamental en el desarrollo de software moderno. Nos permiten garantizar que cada pieza individual de nuestra aplicación funcione correctamente antes de integrarla en el sistema completo. En el ecosistema de Angular, el testing está integrado desde el primer día, pero a medida que las aplicaciones crecen, las necesidades de rendimiento, complejidad y asincronía aumentan exponencialmente.

En este tutorial avanzado, profundizaremos en las técnicas esenciales para escribir pruebas unitarias robustas, eficientes y mantenibles. Aprenderemos a manejar dependencias complejas, flujos reactivos con RxJS, peticiones HTTP y a optimizar nuestra suite de pruebas mediante el uso de herramientas modernas como Jest.

📌 Nota: Este tutorial asume que ya tienes conocimientos básicos sobre Angular y la estructura fundamental de un archivo de pruebas con Jasmine (`describe`, `it`, `expect`).

Configuración del Entorno y TestBed Avanzado

El corazón de las pruebas en Angular es el TestBed. Nos permite configurar y crear un módulo de pruebas virtual para nuestro componente o servicio. Cuando las aplicaciones crecen, configurar el TestBed repetitivamente puede volverse tedioso y lento.

Patrones de Configuración Reutilizables

Para evitar la duplicación de código en tus archivos .spec.ts, es una excelente práctica implementar funciones de configuración auxiliar.

import { TestBed } from '@angular/core/testing';
import { UserService } from './user.service';
import { HttpClientTestingModule } from '@angular/common/http/testing';

describe('UserService - Configuración Avanzada', () => {
  let service: UserService;

  const setup = (providers: any[] = []) => {
    TestBed.configureTestingModule({
      imports: [HttpClientTestingModule],
      providers: [UserService, ...providers]
    });
    service = TestBed.inject(UserService);
  };

  it('debe crearse correctamente', () => {
    setup();
    expect(service).toBeTruthy();
  });
});
1. Configuración TestBed.configureTestingModule() 2. Compilación Compilación de componentes y plantillas 3. Inyección Resolución de dependencias y servicios 4. Ejecución it('debe...', () => { ... }) Ciclo de vida del TestBed en Angular

Aislando Componentes: Mocks, Spies y Stubs

Cuando probamos un componente, rara vez este trabaja de forma aislada. Suele depender de servicios, otros componentes o rutas. Para asegurar que nuestra prueba sea verdaderamente unitaria, debemos aislar el sistema bajo prueba utilizando Mocks y Spies.

Creando Spies Profesionales con Jasmine

Un spy nos permite vigilar funciones, registrar llamadas y alterar su comportamiento de retorno sin necesidad de ejecutar la implementación real.

import { ComponentFixture, TestBed } from '@angular/core/testing';
import { UserProfileComponent } from './user-profile.component';
import { AuthService } from '../auth.service';
import { of } from 'rxjs';

describe('UserProfileComponent', () => {
  let component: UserProfileComponent;
  let fixture: ComponentFixture<UserProfileComponent>;
  let authServiceSpy: jasmine.SpyObj<AuthService>;

  beforeEach(async () => {
    const spy = jasmine.createSpyObj('AuthService', ['getUserProfile', 'updateProfile']);

    await TestBed.configureTestingModule({
      declarations: [UserProfileComponent],
      providers: [{ provide: AuthService, useValue: spy }]
    }).compileComponents();

    authServiceSpy = TestBed.inject(AuthService) as jasmine.SpyObj<AuthService>;
    fixture = TestBed.createComponent(UserProfileComponent);
    component = fixture.componentInstance;
  });

  it('debe cargar el perfil del usuario al inicializarse', () => {
    const mockUser = { id: 1, name: 'Ana Gómez', role: 'Admin' };
    authServiceSpy.getUserProfile.and.returnValue(of(mockUser));

    fixture.detectChanges(); // Dispara ngOnInit

    expect(authServiceSpy.getUserProfile.calls.count()).toBe(1);
    expect(component.user).toEqual(mockUser);
  });
});
💡 Consejo: Utiliza siempre `jasmine.createSpyObj` para servicios con múltiples métodos. Esto previene errores en tiempo de ejecución si el componente intenta acceder a un método del servicio que olvidaste mockear.

Pruebas de Código Asíncrono y RxJS

El manejo de la asincronía es uno de los mayores desafíos al testear aplicaciones Angular. Entre temporizadores (setTimeout), promesas y flujos reactivos de RxJS, nuestras pruebas pueden volverse inestables si no utilizamos las utilidades correctas.

Probando Observables con fakeAsync y tick

Cuando trabajamos con operadores temporales de RxJS como debounceTime, delay o throttleTime, fakeAsync nos permite controlar el tiempo de forma síncrona.

import { fakeAsync, tick } from '@angular/core/testing';
import { SearchService } from './search.service';

describe('SearchService - Operadores Asíncronos', () => {
  let service: SearchService;

  beforeEach(() => {
    service = new SearchService();
  });

  it('debe aplicar debounce a las búsquedas', fakeAsync(() => {
    const results: string[] = [];
    service.searchResults$.forEach(res => results.push(res));

    service.search('Angular');
    tick(150);
    expect(results.length).toBe(0);

    tick(200); // Completa el debounce de 350ms total
    expect(results.length).toBe(1);
    expect(results[0]).toBe('Angular');
  }));
});

Uso de waitForAsync para Operaciones DOM

Para componentes que realizan operaciones pesadas en el DOM o dependen de plantillas externas y hojas de estilo, es necesario utilizar waitForAsync para asegurar que la compilación asíncrona haya finalizado antes de ejecutar los assertions.


Migrando de Karma a Jest: Velocidad y Eficiencia

Tradicionalmente, Angular ha utilizado Karma y Jasmine para la ejecución de pruebas. Sin embargo, Jest se ha convertido en el estándar de la industria debido a su velocidad superior, soporte nativo para TypeScript (vía jest o analogjs), y capacidades avanzadas de mocking de instantáneas (snapshots).

Pasos Básicos para Configurar Jest en un Proyecto Angular

Paso 1: Instalar paquetes necesarios: npm install jest jest-preset-angular @types/jest --save-dev
Paso 2: Configurar el archivo jest.config.js en la raíz del proyecto.
Paso 3: Actualizar el archivo tsconfig.spec.json para incluir los tipos de Jest.
Paso 4: Modificar el script de pruebas en package.json apuntando a Jest.
⚠️ Advertencia: Al migrar de Karma a Jest, algunas funciones específicas del DOM de navegador pueden requerir adaptaciones, ya que Jest utiliza jsdom por defecto para simular el entorno del navegador.

Buenas Prácticas y Cobertura de Código

Una suite de pruebas extensa no sirve de nada si el código que cubre es irrelevante. Debemos apuntar a una cobertura de código significativa, enfocada en la lógica de negocio y los casos límite (edge cases).

Métricas Recomendadas de Cobertura

MétricaObjetivo MínimoDescripción
---------
Sentencias (Statements)85%Porcentaje de instrucciones ejecutadas
Ramas (Branches)80%Cobertura de condiciones if/else y operadores ternarios
---------
Funciones (Functions)90%Funciones y métodos probados
Líneas (Lines)85%Líneas de código efectivas evaluadas
88% Cobertura Global

Consejos Finales para Mantener un Código Testeable

Haz clic para ver las reglas de oro del Clean Code en Testing
  • Mantén las pruebas independientes: Ninguna prueba debe depender del resultado o estado generado por otra.
  • Usa nombres descriptivos: El nombre de la prueba debe explicar claramente el escenario y el resultado esperado (ej: 'debe emitir un evento de error cuando el servidor responda con código 500').
  • Evita lógica compleja en los tests: Las pruebas deben ser lineales y fáciles de leer. No utilices bucles ni condiciones complejas dentro de tus bloques it.

Conclusión

Dominar las pruebas unitarias avanzadas en Angular transforma radicalmente la calidad de tus proyectos. Al dominar el uso de TestBed, aislar componentes con mocks y spies, gestionar la asincronía con precisión y considerar la adopción de herramientas veloces como Jest, garantizas aplicaciones estables, escalables y listas para producción.

Sigue practicando, refactoriza tus pruebas existentes y haz del testing una parte indivisible de tu flujo de desarrollo diario.

Tutoriales relacionados

Comentarios (0)

Aún no hay comentarios. ¡Sé el primero!