Cómo usar waitForAsync y fakeAsync con Angular Testing

Introducción

Índice
  1. Introducción
  • Prerrequisitos
  • Configuración del proyecto
  • Prueba conwaitForAsync
  • Prueba confakeAsync
  • Conclusión
  • Angular 2+ ofrece asyncutilidades fakeAsyncpara probar código asincrónico. Esto debería hacer que escribir pruebas unitarias y de integración de Angular sea mucho más fácil.

    En este artículo, conocerá waitForAsyncalgunas fakeAsyncpruebas de muestra.

    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.4.0, npmv7.19.0 y @angular/corev12.1.1.

    Configuración del proyecto

    Primero, use @angular/clipara crear un nuevo proyecto:

    1. ng new angular-async-fakeasync-example

    Luego, navegue hasta el directorio del proyecto recién creado:

    1. cd angular-async-fakeasync-example

    Esto creará un nuevo proyecto Angular con archivos app.component.html, app.compontent.tsy app.component.spec.ts.

    Prueba conwaitForAsync

    La waitForAsyncutilidad le indica a Angular que ejecute el código en una zona de prueba dedicada que intercepta las promesas. Tratamos brevemente la utilidad async en nuestra introducción a las pruebas unitarias en Angular cuando se usa compileComponents.

    La whenStableutilidad nos permite esperar hasta que se hayan resuelto todas las promesas para ejecutar nuestras expectativas.

    Primero abre y app.component.tsusa Promisea resolve:title

    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 {  title!: string;  setTitle() {    new Promise(resolve = {      resolve('Async Title!');    }).then((val: any) = {      this.title = val;    });  }}

    Luego ábrelo app.component.htmly reemplázalo con un h1y button:

    src/aplicación/aplicación.componente.html

    h1  {{ title }}/h1button (click)="setTitle()"  Set Title/button

    Cuando se hace clic en el botón, la titlepropiedad se establece mediante una promesa.

    Y así es como podemos probar esta funcionalidad usando waitForAsyncy whenStable:

    src/app/app.component.spec.ts

    import { TestBed, waitForAsync } from '@angular/core/testing';import { By } from '@angular/platform-browser';import { AppComponent } from './app.component';describe('AppComponent', () = {  beforeEach(async () = {    await TestBed.configureTestingModule({      declarations: [        AppComponent      ],    }).compileComponents();  });  it('should display title', waitForAsync(() = {    const fixture = TestBed.createComponent(AppComponent);    fixture.debugElement      .query(By.css('.set-title'))      .triggerEventHandler('click', null);    fixture.whenStable().then(() = {      fixture.detectChanges();      const value = fixture.debugElement        .query(By.css('h1'))        .nativeElement        .innerText;      expect(value).toEqual('Async Title!');    });  }));});

    Nota: En una aplicación real, tendrás promesas de que realmente esperan algo útil, como una respuesta de una solicitud a tu API de backend.

    En este punto, puedes ejecutar tu prueba:

    1. ng test

    Esto producirá un 'should display title'resultado de prueba exitoso.

    Prueba confakeAsync

    El problema asynces que todavía tenemos que introducir una espera real en nuestras pruebas, y esto puede hacer que sean muy lentas. fakeAsyncviene al rescate y ayuda a probar código asincrónico de forma sincrónica.

    Para demostrarlo fakeAsync, comencemos con un ejemplo simple. Supongamos que nuestra plantilla de componente tiene un botón que incrementa un valor como este:

    src/aplicación/aplicación.componente.html

    h1  {{ incrementDecrement.value }}/h1button (click)="increment()"  Increment/button

    Llama a un incrementmétodo en la clase del componente que se ve así:

    src/app/app.component.ts

    import { Component } from '@angular/core';import { IncrementDecrementService } from './increment-decrement.service';@Component({  selector: 'app-root',  templateUrl: './app.component.html',  styleUrls: ['./app.component.css']})export class AppComponent {  constructor(public incrementDecrement: IncrementDecrementService) { }  increment() {    this.incrementDecrement.increment();  }}

    Y este método en sí mismo llama a un método en un incrementDecrementservicio:

    1. ng generate service increment-decrement

    Esto tiene un incrementmétodo que se hace asincrónico con el uso de un setTimeout:

    src/app/incremento-decremento.servicio.ts

    import { Injectable } from '@angular/core';@Injectable({  providedIn: 'root'})export class IncrementDecrementService {  value = 0;  message!: string;  increment() {    setTimeout(() = {      if (this.value  15) {        this.value += 1;        this.message = '';      } else {        this.message = 'Maximum reached!';      }    }, 5000); // wait 5 seconds to increment the value  }}

    Obviamente, en una aplicación del mundo real esta asincronicidad se puede introducir de diferentes maneras.

    Ahora usamos fakeAsyncla tickutilidad para ejecutar una prueba de integración y asegurarnos de que el valor se incrementa en la plantilla:

    src/app/app.component.spec.ts

    import { TestBed, fakeAsync, tick } from '@angular/core/testing';import { By } from '@angular/platform-browser';import { AppComponent } from './app.component';describe('AppComponent', () = {  beforeEach(async () = {    await TestBed.configureTestingModule({      declarations: [        AppComponent      ]    }).compileComponents();  });  it('should increment in template after 5 seconds', fakeAsync(() = {    const fixture = TestBed.createComponent(AppComponent);    fixture.debugElement      .query(By.css('button.increment'))      .triggerEventHandler('click', null);    tick(2000);    fixture.detectChanges();    const value1 = fixture.debugElement.query(By.css('h1')).nativeElement.innerText;    expect(value1).toEqual('0'); // value should still be 0 after 2 seconds    tick(3000);    fixture.detectChanges();    const value2 = fixture.debugElement.query(By.css('h1')).nativeElement.innerText;    expect(value2).toEqual('1'); // 3 seconds later, our value should now be 1  }));});

    Observe cómo tickse utiliza la utilidad dentro de un fakeAsyncbloque para simular el paso del tiempo. El argumento que se pasa a tickes la cantidad de milisegundos que deben pasar y estos son acumulativos dentro de una prueba.

    Nota: Tick también se puede utilizar sin argumentos, en cuyo caso espera hasta que se realicen todas las microtareas (cuando se resuelvan las promesas, por ejemplo).

    En este punto, puedes ejecutar tu prueba:

    1. ng test

    Esto producirá un 'should increment in template after 5 seconds'resultado de prueba exitoso.

    Especificar el tiempo de paso de esa manera puede volverse rápidamente complicado y convertirse en un problema cuando no se sabe cuánto tiempo debe pasar.

    En Angular 4.2 se introdujo una nueva utilidad llamada flushque ayuda con ese problema. Simula el paso del tiempo hasta que la cola de macrotareas se vacía. Las macrotareas incluyen cosas como setTimouts, setIntervals, y requestAnimationFrame.

    Entonces, usando flush, podemos escribir una prueba como esta, por ejemplo:

    src/app/app.component.spec.ts

    import { TestBed, fakeAsync, flush } from '@angular/core/testing';import { By } from '@angular/platform-browser';import { AppComponent } from './app.component';describe('AppComponent', () = {  beforeEach(async () = {    await TestBed.configureTestingModule({      declarations: [        AppComponent      ]    }).compileComponents();  });  it('should increment in template', fakeAsync(() = {    const fixture = TestBed.createComponent(AppComponent);    fixture.debugElement      .query(By.css('button.increment'))      .triggerEventHandler('click', null);    flush();    fixture.detectChanges();    const value = fixture.debugElement.query(By.css('h1')).nativeElement.innerText;    expect(value).toEqual('1');  }));});

    En este punto, puedes ejecutar tu prueba:

    1. ng test

    Esto producirá un 'should increment in template'resultado de prueba exitoso.

    Conclusión

    waitForAsyncEn este artículo le presentamos fakeAsyncejemplos de pruebas.

    También puedes consultar la documentación oficial para obtener una guía detallada sobre pruebas de Angular.

    SUSCRÍBETE A NUESTRO BOLETÍN 
    No te pierdas de nuestro contenido ni de ninguna de nuestras guías para que puedas avanzar en los juegos que más te gustan.

    Deja una respuesta

    Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

    Subir

    Este sitio web utiliza cookies para mejorar tu experiencia mientras navegas por él. Este sitio web utiliza cookies para mejorar tu experiencia de usuario. Al continuar navegando, aceptas su uso. Mas informacion