DEV Community

Cover image for Angular + Testing Library + Jest
Alberto Tejero
Alberto Tejero

Posted on

Angular + Testing Library + Jest

¿Por qué usar Testing Library?

Aquí os voy a dar mi opinión de porque usar Testing Library sobre angular, y como digo, es una opinión personal que he experimentado trasteando con Testing Library, aunque tengo que decir que llevo un par de días solamente.

¡OJO! Esto no sustituye al framework que tu uses, sino que le da más funcionalidad o mejor dicho lo suplementa. Es cierto que recomiendan Jest, pero funciona con cualquier framework.

Lo poquito que llevo he visto, aporta un valor increíble a los test de Angular porque se centra en la interacción del usuario con nuestra aplicación. Yo, es cierto que flaqueaba en esta parte del testing, con esto creo que se facilita bastante la historia.

Vamos a comenzar sobre un proyecto de angular vacío…

Quitar Karma y Jasmine

Vamos a usar Jest, por dos razones. Una, porque es lo que recomiendan desde Testing Library y dos, porque en el backend suelo usar Jest.

Desinstalamos Karma y Jasmine:

npm uninstall karma karma-chrome-launcher karma-coverage-istanbul-reporter karma-jasmine karma-jasmine-html-reporter

npm uninstall jasmine-core jasmine-spec-reporter @types/jasmine @types/jasmine
Enter fullscreen mode Exit fullscreen mode

Borramos todas las configuraciones y referencias a Jasmine, Karma y a los ficheros de configuración de éstos.

Ficheros para borrar:

  • karma.conf.js
  • src/test.js

Referencias a src/test.js tenemos en tsconfig.spec.json y en angular.json, de momento borramos el de tsconfig.spec.json, aprovechamos y borramos tambien jasmine del array de types:

Archivo tsconfig.spec.json

Añadir Jest y configurarlo en Angular

En este punto tenemos nuestro proyecto Angular listo para instalarle otro test suite, test runner… en nuestro caso vamos a usar Jest.

Instalamos todo lo necesario para usar Jest en Angular:

npm i -D @types/jest jest jest-preset-angular ts-jest @angular-builders/jest
Enter fullscreen mode Exit fullscreen mode

Configuramos Jest

Al igual que teníamos un fichero de configuración karma.conf.json creamos nuestro fichero de configuración de Jest (jest.config.js).

Fichero de configuración jest.config.js

Aquí cargamos el preset que hemos instalado anteriormente y le decimos el fichero que queremos ejecutar antes de cada ejecución de cualquier test de nuestra suite. En ese fichero solamente tendremos el import del preset para Jest en Angular.

En el fichero tsconfig.spec.json donde antes teníamos jasmine tendremos que añadir nuestro nuevo framework de test Jest en la lista de "types".

Y en tsconfig tendremos que añadir estos flags (esModuleInterop y emitDecortatorMetadata):

Fichero típico de tsconfig

¿Recordáis en pasos anteriores a borrar Karma y Jasmine comenté que, de momento, el fichero angular.json no lo íbamos a tocar?. Pues ahora hay que tocarlo.

Lo que vamos a hacer es cambiar la configuración de test dentro de ese fichero, así que borramos lo que hay dentro de "test" y añadimos la siguiente configuración:

Parte de los tests del fichero angular.json

Lo único que dejamos es el nuevo builder que tenemos instalado.

Falta añadir en nuestro package.json nuestros script para lanzar nuestros test:

"test": "jest --config ./jest.config.js"
Enter fullscreen mode Exit fullscreen mode

¿Y ahora?

Pues vamos a probar a hacer un test simple y vemos si todo está funcionando OK.

YO, he limpiado tanto el componente que crea Angular como su test, bueno su contenido.

Así que se me ha quedado de la siguiente manera el app.component.html

<div>Hola Testing Library en Medium</div>
Enter fullscreen mode Exit fullscreen mode

app.component.spec.ts

import { TestBed, async } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';
import { AppComponent } from './app.component';
describe('AppComponent', () => {
    it('test my first test', async()=> {
        expect(1).toEqual(2);
    });
});
Enter fullscreen mode Exit fullscreen mode

Este test fallará y aquí abajo os pongo la salida con el test fallando y una vez arreglado cambiando el 2 por un 1.
Resultado del test fallando
Resultado del test arreglado y pasando correctamente

¿Donde está Testing Library?, porque yo no lo veo.

Hasta ahora no hemos visto nada de Testing Library, cierto. Así que vamos a instalarlo:

npm install --save-dev @testing-library/angular @testing-library/user-event
Enter fullscreen mode Exit fullscreen mode

Aparte del básico de Angular he instalado user-event ya que lo vamos a usar si o si, como mínimo en este ejemplo.

He creado un segundo componente y este es el test que ha generado Angular-cli:

import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { SecondTestComponent } from './second-test.component';
describe('SecondTestComponent', () => {
    let component: SecondTestComponent;
    let fixture: ComponentFixture<SecondTestComponent>;
    beforeEach(async(() => {
        TestBed.configureTestingModule({
            declarations: [ SecondTestComponent ]
        }).compileComponents();
    }));

    beforeEach(() => {
       fixture = TestBed.createComponent(SecondTestComponent);
       component = fixture.componentInstance;
       fixture.detectChanges();
    });
    it('should create', () => {
        expect(component).toBeTruthy();
    });
});
Enter fullscreen mode Exit fullscreen mode

Esta es la forma "nativa" de Angular que hay para instanciar/crear componentes en nuestro test. Vale, con Testing Library se simplifica la historia y este mismo test se podría quedar en…

import { render, screen } from '@testing-library/angular';
import { SecondTestComponent } from './second-test.component';
describe('SecondTestComponent', () => {
    it('should create', async() => {
        await render(SecondTestComponent);
        expect(screen.getByText('second-test woraks!'));
    });
});
Enter fullscreen mode Exit fullscreen mode

Con esto, que es lo básico vemos que nuestro componente se instancia correctamente.

Por último vamos a crear un input y ver la modificación de una variable al hacer click en un botón.

<p>second-test works!</p>
<form [formGroup]="grettingForm">
    <input type="text" id="name-gretting" name="name" formControlName="name" />
</form>
<button (click)="grettingMe()">Saluda</button>
<div id="gretting">{{gretting}}</div>
Enter fullscreen mode Exit fullscreen mode

El método que ejecutará el botón lo único que hará será "saludar" con el valor del input.

grettingMe(): void {
   this.gretting = `Hola, ${this.grettingForm.controls.name.value}`;
}
Enter fullscreen mode Exit fullscreen mode

Y este es nuestro test

it('should gretting with input value', async () => {
    await render(SecondTestComponent, {
        imports: [ReactiveFormsModule]
    });
    const nameControl = document.querySelector('#name-gretting');
    userEvent.type(nameControl, 'Alberto');
    fireEvent.click(screen.getByText('Saluda'));
    expect(document.querySelector('#gretting').textContent)
    .toEqual('Hola, Alberto');
});
Enter fullscreen mode Exit fullscreen mode
  • Primero renderizamos el componente con los imports necesarios, en este caso el ReactiveFormsModule porque hemos creado un FormGroup.
  • Recuperamos el input mediante querySelector.
  • Con userEvent podemos "simular" las interacciones del usuario en este caso la escritura en nuestro input, con el método type. *Ejecutamos un evento de click sobre el botón. *Y comprobamos que nuestro div contiene el texto del saludo.

Eso sería un test básico, ya de aquí es evolucionar a cosas más complejas como llamadas a servicios y obtener datos de apis… pero con mocks y teniendo componentes que tengan poca responsabilidad, no debería de haber problemas y nuestro código se quedaría limpio, mantenible y super testeado.

En mi canal de Twitch, hice un directo con un componente de un proyecto "real". (Canal twitch).

Os dejo el código en mi repositorio.
Github: Codigo

Podéis encontrarme en mis redes sociales:
Twitter
Instagram
Github
Twitch

Discussion (0)