Escribiendo tests para funciones y propiedades privadas en angular 6

Escribir pruebas unitarias debe convertirse en una tarea diaria, no es posible crear una aplicación en Angular de calidad sin realizar un proceso consciente de unit testing. En este post veremos como probar funciones que retornan propiedades privadas en Angular 6 con Karma y Jasmine.

Vamos a suponer que sabes que es Angular y que sabes como crear un servicio en Angular, por tanto habrás notado que al escribir ng generate service <nombre del servicio> se crean dos archivos, el primero es tu servicio y el segundo notarás que se crea con el mismo nombre de tu servicio pero con la extensión .spec al final, te darás cuenta que lo único que quiere hacer este archivo es proveerte un esqueleto fácil y entendible en el que puedas empezar a escribir pruebas de la manera más fácil posible.

Empecemos por algunos fundamentos. Supongamos que decidiste crear un servicio llamado “names” porque será un servicio que retorne una lista de nombres. Para ello vas a guardar dichos nombres en una propiedad privada y vas crear un metodo get para devolver dichos nombres a un controlador u otro servicio. Sería algo así:

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;
}
}
}

Ahora bien, nuestra primera tentación será escribir en nuestra prueba unitaria algo como esto:

names.service.spec.ts
  • js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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) => {
expect(service).toBeTruthy(); // We hope the service has been inyected and defined
}));

it('should return all names', inject([NamesService], (service: NamesService) => {
expect(service.getNames()).toBe(service.names); // Not allowed
expect(service.getNames()[0]).toBe(service.names[0]); // Not allowed again
expect(service.getNames()[0]).toBe('Mati); // Ok, but Seriously
}));

});

Sin embargo, te darás cuenta de desafortunadamente no puedes acceder a la propiedad names del servicio porque dicha propiedad es privada. Tu test debería estar fallando. Por eso te verás tentado a comparar con los nombres directamente lo cual no resulta ser una buena idea si la cantidad de nombres es muy grande y si cambia constantemente. Por eso lo que tu podrías hacer es omitir a typescript y acceder al objeto mediante la sintaxis de array (cosas que solo javascript nos permite). service["names"] miremos como quedaría:

names.service.spec.ts
  • js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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) => {
expect(service).toBeTruthy(); // We hope the service has been inyected and defined
}));

it('should return all names', inject([NamesService], (service: NamesService) => {
expect(service.getNames().length).toBe(service["names"].length); // OK
expect(service.getNames()[0]).toBe(service["names"][0]); // OK
}));
});

Habrá muchas discusiones sobre si es correcto teóricamente probar las propiedades privadas o no, si es correcto aplicar esta sintaxís o si el test es limpio o no. Sin embargo esto no tiene una respuesta concreta. Dependerá de la filosofía del equipo y de la guía de estilo que tu equipo adopte. Personalmente y en mis equipos de trabajo me gusta tener una navaja suiza de opciones para diferentes casos y tipos de proyecto por lo cual puesde ver esta opción como una herramienta más dentro de tus habilidades de testing.

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: