¿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
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:
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
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).
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):
¿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:
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"
¿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>
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);
});
});
Este test fallará y aquí abajo os pongo la salida con el test fallando y una vez arreglado cambiando el 2 por un 1.
¿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
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();
});
});
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!'));
});
});
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>
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}`;
}
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');
});
- 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
Top comments (0)