Tres maneras de inyectar servicios en unit tests con Angular

Cuando escribimos tests unitarios para nuestros servicios en Angular tenemos varias opciones para inyectar el servicio en cada uno de nuestros “it” bloques, en este post exploraremos cuales son algunas las ventajas y desventajas de cada una de las maneras.

Revisemos un servicio súper simple que deseamos probar, este servicio solo tendrá una propiedad y un método para probar.

names.service.ts
  • js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Observable {
import { Injectable } from '@angular/core';

@Injectable({
providedIn: 'root'
})
export class NamesService {

private names: string[] = ['Juan', 'Mati'];

constructor() { }

public getNames() {
return this.names;
}
}
}

Por defecto nuestro archivo de pruebas asociado nos propone inyectar el servicio directamente en cada bloque “it” de esta manera:

names.service.spec.ts
  • js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import { TestBed, inject } from '@angular/core/testing'; // We always need this
import { NamesService } from './names.service'; // Our service to test

describe('NamesService', () => {
beforeEach(() => {
TestBed.configureTestingModule({
providers: [NamesService] // Service to test
});
});

it('should be created', inject([NamesService], (service: NamesService) => { // Default
it('should be something', inject([NamesService], (service: NamesService) => { // Default
it('should be .....', inject([NamesService], (service: NamesService) => { // Default

/*...*/

Sin embargo. Te darás cuenta que esta manera requiere repetir la misma línea todo el tiempo. Una ventaja de este enfoque es que estas empezando con un servicio nuevo y limpio en cada bloque it. Entonces naturalmente podemos valernos de Angular TestBed para evitar repetir esta línea. Algo así:

names.service.spec.ts
  • js
1
2
3
4
5
6
7
8
9
10
11
beforeEach(() => {
TestBed.configureTestingModule({
providers: [NamesService] // Service to test
});
// It is another approach to put available the service during the tests.
service = TestBed.get(NamesService);
});
/** ... */
it('should be created', ()=> {
// Service is perfectly available
});

TestBed.get() es una función que nos permite crear una instancia del servicio que estamos usando. Al crear el servicio dentro del beforeEach estamos garantizando que cada bloque it tenga una versión nueva del servicio evitando empezar con un servicio corrupto por test anteriores. Sin embargo podrías encontrarte con situaciones que requieran que una misma instancia del servicio y para eso la primera forma inject([NamesService], (service: NamesService) => { se queda corta. Para tener exactamente la misma instancia del servicio a través los bloque it tendriamos que usar el segundo enfoque service = TestBed.get(NamesService); dentro del bloque beforeEach. Veamos un ejemplo de su utilidad con una simple propiedad.

names.service.spec.ts
  • js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
beforeAll(()=> {
service = TestBed.get(NamesService);
});

it('should be myVar set to 13', () => {
service.setMyVar(13);
expect(service.myVar).toBe(13);
});

it('should be myVar still set to 13 and then set to 12', () => {
expect(service.myVar).toBe(13);
service.setMyVar(12);
expect(service.myVar).toBe(12);
});

Así tu puedes persistir el estado del servicio a través de los diferentes its y podría sobre todo ser muy útil en pruebas unitarias relacionadas con procesos de CRUD para evitar repetir código y tener la trazabilidad de todo el test.

Así repasando las tres estrategias para inyectar servicios en nuestros test tenemos:

  • inject([NamesService], (service: NamesService) => {
  • beforeEach(()=>service = TestBed.get(NamesService);}
  • beforeAll(()=>service = TestBed.get(NamesService);}

Eso es todo, 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.

Copyrights © 2018 Sebastian Gomez. All Rights Reserved.

Sobre mí

sebastianMi nombre es Sebastián Gómez, soy ingeniero de sistemas e Informática y Magister en Ingeniería de Sistemas de la Universidad Nacional de Colombia.

Actualmente trabajo en Globant como Web UI Developer con énfasis en aplicaciones híbridas y cross compiladas. Soy el organizador del Google Developers Group de Medellín, así que contactame si quieres dar alguna charla o participar actuamente de esta comunidad.

He participado en una Startup Colombiana llamada SponzorMe al lado de Carlos Rojas y fuí participante de Startup Chile a pesar de no haber continuado con esta startup me apasiona el emprendimiento y me gusta aconsejar y ayudar startups como mentor técnico. También he trabajado en empresas Americanas como StudioHyperset en Estados Unidos y para Measured Medium. Mi interés y mi experiencia es el desarrollo de web y móvil full stack como Front-end con Javascript. Me apasiona desarrollar software, escribir código y enseñar lo que aprendo día a día.

También he trabajado como profesor en diferentes universidades en Medellín Colombia, con tematicas relacionadas con la Inteligencia Artificial, Bases de datos, programación orientada a objetos, minería de datos, desarrollo de software, desarrollo móvil y desarrollo web.

Me encanta escribir código rápido y prototipar de una manera accelerada si quieres ver que hago día a día puedes darle un vistazo a mi codepen:  https://codepen.io/seagomezar/.

Todos los días trato de crear o participar en proyectos, la mayoría open source, así que puede chequear mi GitHub:  https://github.com/seagomezar.

Mi áreas de investigación académica son: Ingeniería de software, Ingeniería de requisitos, procesamiento del lenguaje natural, Ontologías, Bases De Datos,  Machine Learning, Seguimiento de trayectorias y Modelamiento matemático de formaciones.

Estas son algunas de mis publicaciones académicas mas recientes: