👽
3ntr0 Apuntes
  • 📚Principios SOLID y Clean Code
    • 💲Deuda Técnica
    • 📌Mejorar Nombres
      • 📄Nombre según Tipo
      • 🚗Nombre Clases
      • ⚙️Nombres Funciones
    • ☑️Mejorar Funciones
    • ➿Principio DRY
    • 🧼Clean Code en Clases (POO)
      • 📘 Principio de Responsabilidad Única (SRP)
    • 🤮🇸 🇹 🇺 🇵 🇮 🇩 - Code Smells
      • 🚫Singleton
      • 🙏Tight Coupling
      • 🥸U P I D
      • 🚩Más Code Smells
  • 💕Angular
    • 🏍️Standalone
    • 🆚ngClass vs [class.clase]
    • 🩻Directivas
    • 🚩ElementRef
    • 🎀Decoradores
      • @HostListener
    • 🔄OnChanges
    • 🧪Testing - Jasmine y Karma
      • 🚀Módulo 1: Introducción a las pruebas unitarias
      • 🧫Módulo 2: Introducción a Jasmine
      • ⚒️Módulo 3: Introducción a Karma
      • 🔬Módulo 4: Pruebas unitarias en Angular
        • Código comentado Paso a Paso
      • 🕵️‍♂️Módulo 5: Técnicas avanzadas en Jasmine
      • 📚Módulo 6: Técnicas avanzadas en Karma
  • 🖼️HTML y CSS
    • 🖌️Custom Properties
    • Trucos
    • Brakepoints
  • 🧋Javascript
    • 🛠️Funciones
    • 📦Arrays
      • Every y Some
      • Map
      • Reduce
    • 😎Hoisting
    • 🪚Desestructuración
    • 🛻Programación Asincrona
      • 📞Callbacks
      • 🌟Promises
      • 🆚Promise.all Y Promise.any
      • 🚀Async/Await
    • Respuestas HTTP
    • 🧐Dudas Básicas
      • NodeList
      • 🧐Contextos de .this
      • 💭Parametro Rest
      • 🗨️arguments
      • 🙀JavaScript no tiene clases
      • 🆚Null vs Undefined
      • 🔎Operador in
      • 🟨Operador Spread
      • ❓Encadenamiento Opcional
      • 🔲Notación de Corchetes
      • ⛓️Coalescencia Nula (??)
      • 🆚Shallow Copy vs Deep Copy
      • 🆚.json VS JSON.parse
      • ⚙️Fetch wrapper
      • Sets
      • Maps
  • 📒Terminología
    • 💎Esenciales
    • Web
    • Javascript
  • 🌐GIT
    • Source Tree
  • 🧬React
    • 🫂Babel
  • 🔧 Fundamentos de C#
    • 🎛️General
      • 🧐Diferencia Equals y ==
      • 🧐Diferencia entre typeof y .GetType()
      • 📐Convenciones de Nomenclatura
    • 💠Summary
    • 📇.resx
    • 📄Strings
      • ⛓️Comparación de cadenas
    • 🧲Regex
    • 📦POO
      • 🚙Clases
        • ✏️Clase String
        • 📥Métodos de Acceso
        • ⚗️Métodos de Extensión
    • 🖇️Pattern Matching
    • 🚩Excepciones
    • Programación Asíncrona
    • 🔎LINQ
      • 🅿️PLINQ
  • 🌐 Desarrollo Web con ASP.NET
    • 🧬Modelos
      • 🗒️Data Annotations
        • 📑Lista
        • 🧪Atributos
          • 🛠️Atributos Personalizados
          • 🧰AttributeUsage
          • 📥Acceso a los atributos
        • 📚Documentación
    • 👷Servicios
      • ⭐Servicios Singleton
    • ⏳Sesiones
      • 🧭Temp Data
    • DbContext
      • 🔄Eager Loading
    • 🥽Manejo de Datos
      • 🗃️Archivos
        • 📤Subida de Archivos
        • ✏️Leer y Escribir
        • 🕓Manejo de Archivos Temporales
        • 🛡️Validación de Archivos en ASP.NET Core
      • Colecciones
        • 🪜Pila (Stack)
        • 🏇Cola (Queue)
      • 🩻Manejo de XMLs
        • 📂XmlDocument
          • 🎯XPath
        • 🧿XmlReader
    • ❤️Tips y Utilities
      • 🟰StringComparison
    • 🧰Debug Tools
      • 🧭Stopwatch
  • 🚀Razor
    • 🧱Configuración de Proyecto
    • 📃Pages
      • 🔸Método OnGet
      • 🔸Carpeta Models
      • 🔸Partial Pages
    • 🎨Layouts
      • Aplicar CSS A UNA PAGE
    • 🚴‍♂️Routing
    • 🏢_ViewImports
    • ✒️Sintaxis Razor
      • 😀Introducción
      • 📔Expresiones Implícitas
      • 📕Expresiones Explícitas
      • ✍️Renderizar Texto
      • 🧑‍🔬Class Page Model
      • 🔖Tag Helpers
        • 🔹asp-page
        • 🔹asp-append-version
        • 🔹asp-for
        • 🔹asp-items
        • 🔹asp-action y asp-controller
  • 🔮LUA
    • 🎯Fundamentos
    • ⌨️Entrada por Consola
    • 🔗Estructuras de Control
    • ⚒️Funciones
    • 📦Tablas
    • 📚Funciones y Librerías Estándar
    • 🦖POO
Con tecnología de GitBook
En esta página
  • Paso a Paso , Proceso
  • Tabla de Clases y Tipos Usados en Jasmine para Componentes en Angular
  1. Angular
  2. Testing - Jasmine y Karma

Módulo 4: Pruebas unitarias en Angular

⚙️ 4.1. Configuración inicial de pruebas en Angular con Angular CLI

Al generar un nuevo proyecto con ng new, se crean automáticamente los archivos de configuración de Karma y los archivos de pruebas .spec.ts.

Después de generar el proyecto, puedes ejecutar todas las pruebas unitarias con: ng test

Este comando ejecutará Karma y abrirá un navegador para ejecutar las pruebas, mostrando los resultados en tiempo real.

🛠️ 4.2. Pruebas de componentes en Angular

Las pruebas de componentes en Angular se centran en validar el comportamiento de los componentes, verificando su interacción con el template y cómo manejan los datos y eventos.

TestBed es la utilidad principal que nos permite configurar y compilar componentes para pruebas.

Ejemplo de prueba de un componente:

import { ComponentFixture, TestBed } from '@angular/core/testing';
import { MiComponente } from './mi-componente.component';

describe('MiComponente', () => {
  let component: MiComponente;
  let fixture: ComponentFixture<MiComponente>;

  beforeEach(() => {
    TestBed.configureTestingModule({
      declarations: [MiComponente]  // Declaramos el componente a probar
    }).compileComponents();

    fixture = TestBed.createComponent(MiComponente);
    component = fixture.componentInstance;
  });

  // Prueba que verifica que el componente se crea correctamente
  it('debería crear el componente', () => {
    expect(component).toBeTruthy();  // Se espera que el componente exista
  });

  // Prueba para verificar un método del componente
  it('debería sumar correctamente dos números', () => {
    const resultado = component.sumar(2, 3);  // Llamamos a un método del componente
    expect(resultado).toBe(5);  // Esperamos que la suma sea 5
  });
});

Paso a Paso , Proceso

1. Definir la estructura de la suite de pruebas (describe())

describe('SaludoComponent', () => {
  // Aquí dentro irán todas las pruebas para el componente SaludoComponent
});

2. Definir las variables de prueba (let para el componente y el fixture)

let component: SaludoComponent;  // Variable para la instancia del componente
let fixture: ComponentFixture<SaludoComponent>;  // Variable para el fixture que interactúa con el DOM

3. Configurar el entorno de pruebas (beforeEach() con TestBed)

  • Opción componente no Standalone : Hay que declarar el componente

beforeEach(() => {
  TestBed.configureTestingModule({
    declarations: [SaludoComponent]  // Declaramos el componente que vamos a probar
  }).compileComponents();  // Compilamos los componentes

  fixture = TestBed.createComponent(SaludoComponent);  // Creamos una instancia del componente en el fixture
  component = fixture.componentInstance;  // Asignamos la instancia del componente a nuestra variable
  fixture.detectChanges();  // Ejecutamos la detección de cambios para inicializar la vista
});
  • Opción Componentes StandAlone :

  beforeEach(async () => {
    await TestBed.configureTestingModule({
      imports: [SaludoComponent]  // Aquí usamos imports en lugar de declarations
    }).compileComponents();

    fixture = TestBed.createComponent(SaludoComponent);  // Creamos la instancia del componente
    component = fixture.componentInstance;  // Asignamos la instancia a la variable
    fixture.detectChanges();  // Ejecutamos la detección de cambios
  });
  1. Escribir la prueba inicial para verificar que el componente se crea correctamente

it('debería crear el componente', () => {
  expect(component).toBeTruthy();  // Verifica que el componente ha sido creado
});

5. Escribir pruebas para las propiedades del componente

it('debería tener un mensaje inicial "¡Bienvenido a Angular!"', () => {
  expect(component.mensaje).toBe('¡Bienvenido a Angular!');  // Verifica el valor inicial de la propiedad
});

6. Escribir pruebas para los métodos del componente

it('debería cambiar el mensaje cuando se llame a cambiarMensaje()', () => {
  component.cambiarMensaje('¡Nuevo mensaje!');
  expect(component.mensaje).toBe('¡Nuevo mensaje!');  // Verifica que el mensaje cambió correctamente
});

7. Verificar el DOM (Opcional, si interactúas con la vista)

it('debería mostrar el mensaje en el DOM', () => {
  const compiled = fixture.nativeElement;  // Accedemos al DOM del componente
  expect(compiled.querySelector('p').textContent).toContain('¡Bienvenido a Angular!');  // Verificamos el contenido del DOM
});

Tabla de Clases y Tipos Usados en Jasmine para Componentes en Angular

Clase/Tipo
Descripción

ComponentFixture<T>

Proporciona una interfaz para interactuar con un componente de Angular durante las pruebas. Permite acceder a la instancia del componente, manipular el DOM, y ejecutar el ciclo de detección de cambios.

TestBed

Es la clase principal utilizada para configurar y crear entornos de pruebas en Angular. Permite crear instancias de componentes, servicios, y otros elementos inyectables.

DebugElement

Una clase que representa un elemento del DOM con características adicionales para facilitar la depuración en las pruebas. Te permite realizar consultas y obtener acceso a propiedades y eventos.

By

Se utiliza con DebugElement para seleccionar elementos del DOM según un criterio específico, como clase, atributo o directiva.

HarnessLoader

Utilizado para pruebas con el Angular Component Harness, permite acceder a componentes y sus elementos interactivos de manera más abstracta y desacoplada.

RouterTestingModule

Proporciona una configuración de enrutamiento simulada para pruebas unitarias, permitiendo mockear rutas y comportamientos de navegación.

HttpTestingController

Permite probar solicitudes HTTP simuladas en servicios que usan HttpClient. Es parte del módulo HttpClientTestingModule.

SpyObj<T>

Un tipo que representa un objeto espía en Jasmine. Contiene múltiples métodos espiados que puedes configurar y verificar en tus pruebas.

async()

Un tipo de función utilizada para pruebas asíncronas, asegurando que las promesas y eventos se resuelvan antes de validar los resultados.

fakeAsync()

Permite controlar el paso del tiempo en funciones asíncronas simulando temporizadores (setTimeout, setInterval) con tick().

tick()

Funciona junto con fakeAsync() para simular el paso del tiempo en pruebas asíncronas, desencadenando los temporizadores simulados.

ComponentFixture

Es una clase en Angular que proporciona una interfaz para interactuar con componentes en pruebas unitarias.

Funciones principales de ComponentFixture:

  • Acceso al componente: A través de ComponentFixture, puedes acceder a la instancia del componente que estás probando, permitiéndote llamar sus métodos y verificar su estado.

  • Acceso al DOM: También te permite interactuar con el DOM asociado al componente, lo que es útil para verificar cambios en la vista (HTML), como el binding de datos o la respuesta a eventos del usuario.

  • Detección de cambios: Proporciona un método llamado detectChanges() que se usa para ejecutar el ciclo de detección de cambios de Angular. Esto es importante en las pruebas, ya que asegura que los cambios en el estado del componente se reflejen en la vista.

Propiedades y métodos clave:

  • componentInstance: Contiene la instancia del componente, lo que te permite acceder directamente a sus propiedades y métodos.

  • nativeElement: Proporciona acceso al elemento DOM que representa el componente, permitiendo interactuar con su estructura HTML.

  • debugElement: Es una envoltura de nativeElement que ofrece funcionalidades adicionales para interactuar con el DOM y realizar consultas más complejas (por ejemplo, buscar elementos por atributos o clases).

  • detectChanges(): Dispara la detección de cambios, asegurando que cualquier cambio en los datos se refleje en la vista.

TestBed

TestBed es una clase fundamental en Angular que se utiliza para configurar y crear entornos de pruebas unitarias. Es el motor que permite la inyección de dependencias, la configuración de módulos y la creación de instancias de componentes o servicios en un entorno aislado de pruebas.

Funciones principales de TestBed:

  • Configuración de módulos y componentes: Te permite definir qué componentes, directivas, servicios o módulos estarán disponibles en las pruebas, utilizando TestBed.configureTestingModule().

  • Creación de instancias: Facilita la creación de componentes, servicios y otras dependencias, usando métodos como createComponent() y inject().

  • Mock de dependencias: Permite mockear servicios o dependencias utilizando el patrón de proveedores, permitiendo el reemplazo de implementaciones reales con mocks o spies.

Propiedades y métodos clave:

  • configureTestingModule(): Configura el entorno de pruebas proporcionando los módulos, componentes y servicios necesarios.

  • createComponent(): Crea una instancia de un componente para realizar pruebas.

  • inject(): Proporciona acceso a los servicios registrados en el entorno de pruebas, permitiendo inyectarlos en las pruebas.

DebugElement

DebugElement es una clase que representa un elemento del DOM con capacidades adicionales para facilitar la depuración durante las pruebas. Proporciona acceso a las propiedades, eventos y atributos de un elemento HTML en una prueba.

Funciones principales de DebugElement:

  • Acceso avanzado al DOM: Ofrece más herramientas que nativeElement, permitiendo realizar consultas complejas y acceder a atributos y propiedades internas del DOM.

  • Simulación de eventos: Facilita la simulación de eventos como click, input, entre otros.

  • Interacción con componentes hijos: Permite acceder a los elementos hijos de un componente y realizar búsquedas dentro del árbol de DOM.

Propiedades y métodos clave:

  • nativeElement: El elemento DOM nativo al que representa DebugElement, con acceso directo a los métodos y propiedades del DOM.

  • triggerEventHandler(): Simula eventos como click o input, desencadenando la respuesta del componente.

  • query() y queryAll(): Permiten buscar elementos específicos en el DOM usando selectores.

By

By es una clase utilizada en pruebas de Angular junto con DebugElement para realizar búsquedas en el DOM. Permite seleccionar elementos basados en distintos criterios como clase, directiva, o nombre de etiqueta.

Funciones principales de By:

  • Selección por atributos y directivas: Facilita la selección de elementos del DOM que contienen una clase CSS específica, directiva o atributo.

  • Interacción con DebugElement: Se utiliza junto a DebugElement para realizar búsquedas complejas dentro de un componente y acceder a elementos específicos para las pruebas.

Propiedades y métodos clave:

  • By.css(): Selecciona elementos en base a un selector CSS.

  • By.directive(): Selecciona elementos que contienen una directiva Angular.

  • By.all(): Permite seleccionar múltiples elementos en el DOM que coincidan con un selector.

HttpTestingController

HttpTestingController es una clase utilizada para realizar pruebas sobre solicitudes HTTP simuladas en Angular. Se utiliza principalmente cuando los servicios interactúan con APIs externas a través de HttpClient.

Funciones principales de HttpTestingController:

  • Interceptación de solicitudes HTTP: Permite simular y capturar solicitudes HTTP realizadas por el servicio y verificar su correcto funcionamiento.

  • Control de las respuestas: Simula respuestas exitosas o fallidas para verificar cómo el servicio maneja diferentes situaciones.

  • Verificación de solicitudes no realizadas: Permite verificar que no haya solicitudes HTTP pendientes o inesperadas.

Propiedades y métodos clave:

  • expectOne(): Verifica que una única solicitud HTTP haya sido realizada con una URL específica.

  • flush(): Proporciona una respuesta simulada para la solicitud capturada.

  • verify(): Asegura que no haya solicitudes HTTP pendientes al final de una prueba.

SpyObj

SpyObj es un tipo utilizado en Jasmine para crear objetos espía con múltiples métodos espiados. Esto es útil para pruebas donde quieres observar el comportamiento de un objeto con múltiples métodos, como un servicio.

Funciones principales de SpyObj:

  • Creación de objetos espía: Permite crear un objeto con múltiples métodos espiados, reemplazando la funcionalidad original por spies.

  • Simulación de respuestas: Facilita la simulación de diferentes comportamientos para los métodos de los objetos espiados, como devolver valores específicos o lanzar excepciones.

Propiedades y métodos clave:

  • createSpyObj(): Crea un objeto con uno o más métodos espías.

  • and.returnValue(): Simula que un método espía devuelve un valor específico.

  • and.throwError(): Simula que un método espía lanza un error.

  • calls.reset(): Restablece el historial de llamadas del espía.

⚡ 4.3. Pruebas de servicios en Angular

Los servicios en Angular suelen contener lógica de negocio y se prueban inyectándolos en las pruebas usando TestBed. A menudo interactúan con dependencias externas, como APIs, por lo que es común usar mocks o spies para simular estas interacciones.

Ejemplo de prueba de un servicio:

import { TestBed } from '@angular/core/testing';
import { MiServicio } from './mi-servicio.service';

describe('MiServicio', () => {
  let servicio: MiServicio;

  beforeEach(() => {
    TestBed.configureTestingModule({
      providers: [MiServicio]  // Registramos el servicio a probar
    });
    servicio = TestBed.inject(MiServicio);
  });

  // Verifica que el servicio esté creado
  it('debería crear el servicio', () => {
    expect(servicio).toBeTruthy();  // Se espera que el servicio exista
  });

  // Prueba de un método del servicio
  it('debería sumar correctamente dos números', () => {
    const resultado = servicio.sumar(2, 3);  // Llamamos a un método del servicio
    expect(resultado).toBe(5);  // Esperamos que la suma sea 5
  });
});

🔄 4.4. Pruebas con dependencias simuladas (Mocks y Spies)

Es común que los componentes y servicios tengan dependencias, como otros servicios o APIs. Para evitar probar el comportamiento de las dependencias, usamos mocks o spies para simular su comportamiento.

En Angular, puedes usar spyOn para crear espías que simulen métodos y controlar su comportamiento.

Ejemplo de prueba con un servicio mockeado:

import { TestBed } from '@angular/core/testing';
import { MiComponente } from './mi-componente.component';
import { MiServicio } from './mi-servicio.service';

describe('MiComponente con servicio mockeado', () => {
  let component: MiComponente;
  let servicio: MiServicio;

  beforeEach(() => {
    const servicioMock = {  // Mock del servicio con un método simulado
      sumar: jasmine.createSpy('sumar').and.returnValue(10)
    };

    TestBed.configureTestingModule({
      declarations: [MiComponente],
      providers: [{ provide: MiServicio, useValue: servicioMock }]
    }).compileComponents();

    component = TestBed.createComponent(MiComponente).componentInstance;
    servicio = TestBed.inject(MiServicio);
  });

  // Prueba donde el servicio está mockeado
  it('debería obtener 10 de la función sumar del servicio mockeado', () => {
    const resultado = component.usarServicioParaSumar(2, 3);  // Método que usa el servicio
    expect(resultado).toBe(10);  // Esperamos que el mock retorne 10
    expect(servicio.sumar).toHaveBeenCalledWith(2, 3);  // Verificamos que el servicio fue llamado
  });
});

🔀 4.5. Pruebas con Observables y Promesas

Muchas veces en Angular se trabaja con Observables (por ejemplo, para interactuar con APIs) o Promesas. Las pruebas deben manejar correctamente estas respuestas asíncronas.

Para probar código asíncrono en Jasmine, puedes usar la función fakeAsync() y la utilidad tick() para simular el paso del tiempo, o async() para manejar operaciones con await.

Ejemplo de prueba de un Observable:

import { of } from 'rxjs';
import { MiServicio } from './mi-servicio.service';

describe('Prueba con Observables', () => {
  let servicio: MiServicio;

  beforeEach(() => {
    servicio = new MiServicio();
  });

  it('debería manejar un Observable correctamente', (done: DoneFn) => {
    const observable$ = of(5);  // Simulamos un Observable que emite el valor 5

    observable$.subscribe(valor => {
      expect(valor).toBe(5);  // Esperamos que el valor emitido sea 5
      done();  // Marcamos la prueba como completada
    });
  });
});

🛠️ 4.6. Pruebas de directivas y pipes personalizados

En Angular, las directivas y los pipes son piezas clave para manipular el DOM y transformar datos. Las pruebas para estos elementos aseguran que su funcionalidad es la correcta.

Ejemplo de prueba de un pipe personalizado:

import { MiPipe } from './mi-pipe.pipe';

describe('MiPipe', () => {
  let pipe: MiPipe;

  beforeEach(() => {
    pipe = new MiPipe();
  });

  it('debería transformar correctamente el valor', () => {
    const resultado = pipe.transform('hola');  // Transformamos el valor con el pipe
    expect(resultado).toBe('Hola');  // Esperamos el valor transformado
  });
});

AnteriorMódulo 3: Introducción a KarmaSiguienteCódigo comentado Paso a Paso

Última actualización hace 9 meses

💕
🧪
🔬