Cómo empezar a utilizar pruebas unitarias en Angular

Introducción
Si su proyecto se creó utilizando Angular CLI , todo estará listo para que pueda comenzar a escribir pruebas utilizando Jasmine como marco de prueba y Karma como ejecutor de pruebas.
Angular también proporciona utilidades como TestBedy asyncpara facilitar la prueba de código asincrónico, componentes, directivas o servicios.
En este artículo, aprenderá a escribir y ejecutar pruebas unitarias en Angular usando Jasmine y Karma.
Prerrequisitos
Para completar este tutorial, necesitarás:
- Node.js instalado localmente, lo cual puedes hacer siguiendo Cómo instalar Node.js y crear un entorno de desarrollo local .
- Algunos conocimientos sobre la configuración de un proyecto Angular .
Este tutorial fue verificado con Node v16.2.0, npmv7.15.1 y @angular/corev12.0.4.
Paso 1: Configuración del proyecto
Los archivos de prueba generalmente se colocan junto a los archivos que prueban, pero también pueden estar en su propio directorio separado si lo prefiere.
Estos archivos de especificaciones utilizan la convención de nombres *.spec.ts.
Primero, use @angular/clipara crear un nuevo proyecto:
- ng new angular-unit-test-example
Luego, navegue hasta el directorio del proyecto recién creado:
- cd angular-unit-test-example
Junto a app.component, habrá un app.component.spec.tsarchivo. Abra este archivo y examine su contenido:
src/app/app.component.spec.ts
import { TestBed } from '@angular/core/testing';import { AppComponent } from './app.component';describe('AppComponent', () = { beforeEach(async () = { await TestBed.configureTestingModule({ declarations: [ AppComponent ], }).compileComponents(); }); it('should create the app', () = { const fixture = TestBed.createComponent(AppComponent); const app = fixture.componentInstance; expect(app).toBeTruthy(); }); it(`should have as title 'angular-unit-test-example'`, () = { const fixture = TestBed.createComponent(AppComponent); const app = fixture.componentInstance; expect(app.title).toEqual('angular-unit-test-example'); }); it('should render title', () = { const fixture = TestBed.createComponent(AppComponent); fixture.detectChanges(); const compiled = fixture.nativeElement; expect(compiled.querySelector('.content span').textContent).toContain('angular-unit-test-example app is running!'); });});
Entendiendo a Jasmine
Primero, algunas cosas que es importante saber sobre el jazmín:
describeLos bloques definen un conjunto de pruebas y cadaitbloque es para una prueba individual.beforeEachSe ejecuta antes de cada prueba y se utiliza para lasetupparte de una prueba.afterEachSe ejecuta después de cada prueba y se utiliza para lateardownparte de una prueba.- También puedes utilizar
beforeAllyafterAll, y estos se ejecutan una vez antes o después de todas las pruebas. - Para probar una afirmación en Jasmine,
expectutilice y un comparador comotoBeDefined,toBeTruthy,toContain,toEqual,toThrow,toBeNull, … Por ejemplo:expect(myValue).toBeGreaterThan(3); - Puedes hacer una afirmación negativa con
not:expect(myValue).not.toBeGreaterThan(3); - También puedes definir comparadores personalizados.
TestBedes la utilidad principal disponible para las pruebas específicas de Angular. La usarás TestBed.configureTestingModuleen beforeEachel bloque de tu conjunto de pruebas y le darás un objeto con valores similares a los de un componente regular NgModulepara declarations, providersy imports. Luego puedes encadenar una llamada a compileComponentspara indicarle a Angular que compile los componentes declarados.
Puede crear un component fixturecon TestBed.createComponent. Los accesorios tienen acceso a un debugElement, lo que le dará acceso a los componentes internos del accesorio.
La detección de cambios no se realiza automáticamente, por lo que deberá llamar detectChangesa un accesorio para indicarle a Angular que ejecute la detección de cambios.
Envolver la función de devolución de llamada de una prueba o el primer argumento de beforeEachwith asyncpermite a Angular realizar una compilación asincrónica y esperar hasta que el contenido dentro del asyncbloque esté listo antes de continuar.
Entendiendo las pruebas
Esta primera prueba se denomina should create the appy se utiliza expectpara verificar la presencia del componente con toBeTruthy().
La segunda prueba se nombra should have as title 'angular-unit-test-example'y se utiliza expectpara verificar que el app.titlevalor sea igual a la cadena 'angular-unit-test-example'con toEqual().
La tercera prueba se nombra should render titley se utiliza expectpara comprobar el código compilado para el texto 'angular-unit-test-example app is running!'con toContain().
En su terminal, ejecute el siguiente comando:
- ng test
Se ejecutarán las tres pruebas y aparecerán los resultados de las mismas:
Output3 specs, 0 failures, randomized with seed 84683AppComponent* should have as title 'angular-unit-test-example'* should create the app* should render title
Las tres pruebas se están superando actualmente.
Paso 2: creación de un componente de ejemplo
Creemos un componente que incremente o disminuya un valor.
Abra app.component.tssu editor de código y reemplace las siguientes líneas de código con la lógica incrementy decrement:
src/app/app.component.ts
import { Component } from '@angular/core';@Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css']})export class AppComponent { value = 0; message!: string; increment() { if (this.value 15) { this.value += 1; this.message = ''; } else { this.message = 'Maximum reached!'; } } decrement() { if (this.value 0) { this.value -= 1; this.message = ''; } else { this.message = 'Minimum reached!'; } }}
Ábrelo app.component.htmlen tu editor de código y reemplaza el contenido con el siguiente código:
src/aplicación/aplicación.componente.html
h1{{ value }}/h1hrbutton (click)="increment()"Increment/buttonbutton (click)="decrement()"Decrement/buttonp {{ message }}/p
En este punto, deberías tener versiones revisadas de app.component.tsy app.component.html.
Paso 3: creación del conjunto de pruebas
Vuelva a visitar app.component.spec.tssu editor de código y reemplácelo con estas líneas de código:
src/app/app.component.spec.ts
import { TestBed, async, ComponentFixture } from '@angular/core/testing';import { By } from '@angular/platform-browser';import { DebugElement } from '@angular/core';import { AppComponent } from './app.component';describe('AppComponent', () = { let fixture: ComponentFixtureAppComponent; let debugElement: DebugElement; beforeEach(async(() = { TestBed.configureTestingModule({ declarations: [ AppComponent ], }).compileComponents(); fixture = TestBed.createComponent(AppComponent); debugElement = fixture.debugElement; })); it('should increment and decrement value', () = { fixture.componentInstance.increment(); expect(fixture.componentInstance.value).toEqual(1); fixture.componentInstance.decrement(); expect(fixture.componentInstance.value).toEqual(0); }); it('should increment value in template', () = { debugElement .query(By.css('button.increment')) .triggerEventHandler('click', null); fixture.detectChanges(); const value = debugElement.query(By.css('h1')).nativeElement.innerText; expect(value).toEqual('1'); }); it('should stop at 0 and show minimum message', () = { debugElement .query(By.css('button.decrement')) .triggerEventHandler('click', null); fixture.detectChanges(); const message = debugElement.query(By.css('p.message')).nativeElement.innerText; expect(fixture.componentInstance.value).toEqual(0); expect(message).toContain('Minimum'); }); it('should stop at 15 and show maximum message', () = { fixture.componentInstance.value = 15; debugElement .query(By.css('button.increment')) .triggerEventHandler('click', null); fixture.detectChanges(); const message = debugElement.query(By.css('p.message')).nativeElement.innerText; expect(fixture.componentInstance.value).toEqual(15); expect(message).toContain('Maximum'); });});
Asignamos fixturey debugElementdirectamente en el beforeEachbloque porque todas nuestras pruebas los necesitan. También los tipificamos fuertemente al importar ComponentFixturedesde @angular/core/testingy DebugElementdesde @angular/core.
En nuestra primera prueba, llamamos a métodos en la propia instancia del componente.
En las pruebas restantes, usamos nuestro DebugElementpara activar los clics de los botones. Observe cómo DebugElementtiene un querymétodo que toma un predicado. Aquí usamos la Byutilidad y su cssmétodo para encontrar un elemento específico en la plantilla. DebugElementtambién tiene un nativeElementmétodo para acceder directamente al DOM.
También usamos fixture.detectChangesen las últimas 3 pruebas para indicarle a Angular que ejecute la detección de cambios antes de realizar nuestras afirmaciones con Jasmine expect.
Una vez que hayas realizado los cambios, ejecuta el ng testcomando desde la terminal:
- ng test
Esto iniciará Karma en modo de observación, por lo que sus pruebas se volverán a compilar cada vez que cambie un archivo.
Output4 specs, 0 failures, randomized with seed 27239AppComponent* should increment value in template* should increment and decrement value* should stop at 0 and show minimum message* should stop at 15 and show maximum message
Las cuatro pruebas serán aprobadas.
Conclusión
En este artículo, aprenderá a escribir y ejecutar pruebas unitarias en Angular con Jasmine y Karma. Ahora que conoce las principales utilidades de prueba de Angular, puede comenzar a escribir pruebas para componentes simples.
Continúe su aprendizaje probando componentes con dependencias, probando servicios y utilizando mocks, stubs y spys.
También puedes consultar la documentación oficial para obtener una guía detallada sobre pruebas de Angular.

Deja una respuesta