Sebastian Gomez
Cuando escribimos tests unitarios para nuestros servicios en Angular, tenemos varias opciones para inyectar el servicio en cada uno de nuestros bloques "it". En este post, exploraremos algunas ventajas y desventajas de cada una de las maneras. 💡
📚 Un vistazo al servicio que deseamos probarRevisemos un servicio súper simple que deseamos probar; este servicio solo tendrá una propiedad y un método para probar:
// Importa el decorador Injectable desde el paquete @angular/core
import { Injectable } from '@angular/core';
// Define una clase llamada "Observable" (aunque parece que no es utilizada y podría eliminarse)
class Observable {
}
// Utiliza el decorador Injectable para indicar que la clase NamesService puede ser inyectada en otras partes de la aplicación
@Injectable({
providedIn: 'root' // Especifica que esta clase debe proporcionarse en el nivel raíz (root) del módulo de inyección de dependencias
})
// Exporta la clase NamesService para que pueda ser importada y utilizada en otras partes de la aplicación
export class NamesService {
// Declara una propiedad privada "names" que es un array de strings e inicializa con dos nombres: 'Juan' y 'Mati'
private names: string[] = ['Juan', 'Mati'];
// Define el constructor de la clase NamesService (está vacío en este caso)
constructor() { }
// Define un método público llamado "getNames" que retorna el array de nombres
public getNames() {
return this.names;
}
}
📝 Forma 1: Inyectar el servicio directamente en cada bloque "it"Por defecto, nuestro archivo de pruebas asociado nos propone inyectar el servicio directamente en cada bloque "it" de esta manera:
// Importa TestBed e inject desde el paquete @angular/core/testing, necesarios para realizar pruebas en Angular
import { TestBed, inject } from '@angular/core/testing';
// Importa la clase NamesService desde el archivo './names.service'
import { NamesService } from './names.service';
// Define el conjunto de pruebas para la clase NamesService
describe('NamesService', () => {
// Antes de cada prueba, configura el módulo de pruebas con TestBed
beforeEach(() => {
TestBed.configureTestingModule({
providers: [NamesService] // Registra NamesService como un proveedor de servicios en el módulo de pruebas
});
});
// Define una prueba que verifica si la instancia del servicio NamesService se crea correctamente
it('should be created', inject([NamesService], (service: NamesService) => {
// El código de la prueba iría aquí
}));
// Define una prueba personalizada (parece incompleta y requiere más detalles sobre lo que se espera probar)
it('should be something', inject([NamesService], (service: NamesService) => {
// El código de la prueba iría aquí
}));
// Define otra prueba personalizada (también parece incompleta y requiere más detalles sobre lo que se espera probar)
it('should be .....', inject([NamesService], (service: NamesService) => {
// El código de la prueba iría aquí
}));
});
🚀 Ventaja: Empezamos con un servicio nuevo y limpio en cada bloque "it".
🚧 Desventaja: Requiere repetir la misma línea todo el tiempo.
📝 Forma 2: Usar Angular TestBed para evitar repetir la líneaPodemos valernos de Angular TestBed para evitar repetir esta línea. Algo así:
// Antes de cada prueba, ejecuta la siguiente función anónima
beforeEach(() => {
// Configura el módulo de pruebas con TestBed
TestBed.configureTestingModule({
providers: [NamesService], // Registra NamesService como un proveedor de servicios en el módulo de pruebas
});
// Es otra forma de poner a disposición el servicio durante las pruebas
// Obtiene una instancia de NamesService utilizando TestBed.get()
service = TestBed.get(NamesService);
});
// Define una prueba (se omite parte del código) ...
it("should be created", () => {
// El servicio está perfectamente disponible
// El código de la prueba para verificar si la instancia del servicio se crea correctamente iría aquí
});
🚀 Ventaja: Cada bloque "it" tiene una versión nueva del servicio, evitando empezar con un servicio corrupto por tests anteriores.
🚧 Desventaja: No permite compartir la misma instancia del servicio entre diferentes bloques "it".
📝 Forma 3: Persistir el estado del servicio a través de los diferentes "it"Para tener exactamente la misma instancia del servicio a través de los bloques "it", podemos usar el siguiente enfoque:
// Antes de todas las pruebas, ejecuta la siguiente función anónima
beforeAll(() => {
// Obtiene una instancia de NamesService utilizando TestBed.get()
service = TestBed.get(NamesService);
});
// Define una prueba que verifica si el valor de la propiedad myVar se establece en 13
it("should be myVar set to 13", () => {
// Llama al método setMyVar() del servicio con el argumento 13
service.setMyVar(13);
// Espera que el valor de la propiedad myVar del servicio sea igual a 13
expect(service.myVar).toBe(13);
});
// Define una prueba que verifica si el valor de la propiedad myVar todavía está establecido en 13 y luego se establece en 12
it("should be myVar still set to 13 and then set to 12", () => {
// Espera que el valor de la propiedad myVar del servicio sea igual a 13
expect(service.myVar).toBe(13);
// Llama al método setMyVar() del servicio con el argumento 12
service.setMyVar(12);
// Espera que el valor de la propiedad myVar del servicio sea igual a 12
expect(service.myVar).toBe(12);
});
🚀 Ventaja: Permite persistir el estado del servicio a través de los diferentes "it", lo cual puede ser útil en pruebas unitarias relacionadas con procesos de CRUD para evitar repetir código y tener la trazabilidad de todo el test.
🚧 Desventaja: Puede hacer que los tests sean más difíciles de mantener si no se maneja adecuadamente la persistencia del estado.
🎓 ConclusionesRepasando las tres estrategias para inyectar servicios en nuestros tests, tenemos:
Espero que este post te sea de utilidad. Si tienes alguna duda, no dudes en dejarme un comentario en la parte de abajo. Recuerda que si te gustó, también puedes compartir usando los links a las redes sociales en la parte de abajo. 😄
Creador de contenido principalmente acerca de tecnología.