Un componente, a diferencia de todas las demás partes de una aplicación Angular, combina una plantilla HTML y una clase TypeScript. El componente es realmente la plantilla y la clase. trabajando juntos. Para probar adecuadamente un componente, debe probar que funcionan juntos como se espera.

Dichas pruebas requieren crear el elemento host del componente en el DOM del navegador, como hace Angular, e investigar la interacción de la clase del componente con el DOM como se describe en su plantilla.

El Angular TestBed facilita este tipo de pruebas, como verá en las secciones siguientes. Pero en muchos casos probando la clase de componente solo, sin la participación de DOM, puede validar gran parte del comportamiento del componente de una manera más fácil y obvia.

Para ver la aplicación de muestra que describen las guías de prueba, consulte la aplicación de muestra.

Para conocer las características de las pruebas en las guías de prueba, consulte pruebas.

Prueba de clase de componente

Pruebe una clase de componente por sí sola como lo haría con una clase de servicio.

Las pruebas de clases de componentes deben mantenerse muy limpias y sencillas. Debe probar solo una unidad. A primera vista, debería poder comprender lo que está probando la prueba.

Considera esto LightswitchComponent que enciende y apaga una luz (representada por un mensaje en pantalla) cuando el usuario hace clic en el botón.

@Component(
  selector:'lightswitch-comp',
  template:`
    
    message`)exportclassLightswitchComponent
  isOn =false;clicked()this.isOn =!this.isOn;getmessage()return`The light is $this.isOn ?'On':'Off'`;

Puede decidir solo probar que el clicked() El método alterna la luz encendido apagado Estado y establece el mensaje de forma adecuada.

Esta clase de componente no tiene dependencias. Para probar este tipo de clases, siga los mismos pasos que usaría para un servicio que no tiene dependencias:

  1. Cree un componente con la nueva palabra clave.
  2. Empuje su API.
  3. Afirmar expectativas sobre su estado público.
describe('LightswitchComp',()=>it('#clicked() should toggle #isOn',()=>const comp =newLightswitchComponent();expect(comp.isOn).toBe(false,'off at first');
    comp.clicked();expect(comp.isOn).toBe(true,'on after click');
    comp.clicked();expect(comp.isOn).toBe(false,'off after second click'););it('#clicked() should set #message to "is on"',()=>const comp =newLightswitchComponent();expect(comp.message).toMatch(/is off/i,'off at first');
    comp.clicked();expect(comp.message).toMatch(/is on/i,'on after clicked');););

Aquí está el DashboardHeroComponent desde el Tour de héroes tutorial.

exportclassDashboardHeroComponent@Input() hero: Hero;@Output() selected =newEventEmitter<Hero>();click()this.selected.emit(this.hero);

Aparece dentro de la plantilla de un componente principal, que une un héroe al @Input propiedad y escucha un evento generado a través de la seleccionado@Output propiedad.

Puede probar que el código de la clase funciona sin crear el DashboardHeroComponent o su componente principal.

it('raises the selected event when clicked',()=>const comp =newDashboardHeroComponent();const hero: Hero =id:42, name:'Test';
  comp.hero = hero;

  comp.selected.subscribe((selectedHero: Hero)=>expect(selectedHero).toBe(hero));
  comp.click(););

Cuando un componente tiene dependencias, es posible que desee utilizar la TestBed para crear el componente y sus dependencias.

El seguimiento WelcomeComponent depende de UserService para saber el nombre del usuario a saludar.

exportclassWelcomeComponentimplementsOnInit
  welcome:string;constructor(private userService: UserService)ngOnInit():voidthis.welcome =this.userService.isLoggedIn ?'Welcome, '+this.userService.user.name :'Please log in.';

Puede comenzar creando una simulación del UserService que cubra las necesidades mínimas de este componente.

classMockUserService
  isLoggedIn =true;
  user = name:'Test User';

Luego proporcione e inyecte amboscomponentey el servicio en el TestBed configuración.

beforeEach(()=>
  TestBed.configureTestingModule(// provide the component-under-test and dependent service
    providers:[
      WelcomeComponent, provide: UserService, useClass: MockUserService ]);// inject both the component and the dependent service.
  comp = TestBed.inject(WelcomeComponent);
  userService = TestBed.inject(UserService););

Luego ejercite la clase de componente, recordando llamar a los métodos de enlace del ciclo de vida como lo hace Angular cuando ejecuta la aplicación.

it('should not have welcome message after construction',()=>expect(comp.welcome).toBeUndefined(););it('should welcome logged in user after Angular calls ngOnInit',()=>
  comp.ngOnInit();expect(comp.welcome).toContain(userService.user.name););it('should ask user to log in if not logged in after ngOnInit',()=>
  userService.isLoggedIn =false;
  comp.ngOnInit();expect(comp.welcome).not.toContain(userService.user.name);expect(comp.welcome).toContain('log in'););

Prueba de DOM de componentes

Probando el componente clase es tan fácil como probar un servicio.

Pero un componente es más que solo su clase. Un componente interactúa con el DOM y con otros componentes. los solo clase las pruebas pueden informarle sobre el comportamiento en clase. No pueden decirle si el componente se procesará correctamente, responderá a la entrada y los gestos del usuario, o si se integrará con sus componentes padre e hijo.

Ninguno de los solo clase Las pruebas anteriores pueden responder preguntas clave sobre cómo se comportan realmente los componentes en la pantalla.

  • Es Lightswitch.clicked() vinculado a algo tal que el usuario pueda invocarlo?
  • Es el Lightswitch.message ¿desplegado?
  • ¿Puede el usuario seleccionar realmente el héroe mostrado por DashboardHeroComponent?
  • ¿Se muestra el nombre del héroe como se esperaba (es decir, en mayúsculas)?
  • ¿Es el mensaje de bienvenida que muestra la plantilla de WelcomeComponent?

Es posible que estas no sean preguntas preocupantes para los componentes simples ilustrados anteriormente. Pero muchos componentes tienen interacciones complejas con los elementos DOM descritos en sus plantillas, lo que hace que HTML aparezca y desaparezca a medida que cambia el estado del componente.

Para responder a este tipo de preguntas, debe crear los elementos DOM asociados con los componentes, debe examinar el DOM para confirmar que el estado del componente se muestra correctamente en los momentos adecuados y debe simular la interacción del usuario con la pantalla para determinar si esas interacciones hacer que el componente se comporte como se esperaba.

Para escribir este tipo de prueba, utilizará funciones adicionales del TestBed así como otros ayudantes de prueba.

Pruebas generadas por CLI

La CLI crea un archivo de prueba inicial para usted de forma predeterminada cuando le solicita que genere un nuevo componente.

Por ejemplo, el siguiente comando CLI genera un BannerComponent en el app/banner carpeta (con plantilla y estilos en línea):

ng generate component banner --inline-template --inline-style --module app

También genera un archivo de prueba inicial para el componente, banner-external.component.spec.ts, que se ve así:

import ComponentFixture, TestBed, waitForAsync from'@angular/core/testing';import BannerComponent from'./banner.component';describe('BannerComponent',()=>let component: BannerComponent;let fixture: ComponentFixture<BannerComponent>;beforeEach(waitForAsync(()=>
    TestBed.configureTestingModule(declarations:[BannerComponent]).compileComponents();));beforeEach(()=>
    fixture = TestBed.createComponent(BannerComponent);
    component = fixture.componentInstance;
    fixture.detectChanges(););it('should create',()=>expect(component).toBeDefined();););

Porque compileComponents es asincrónico, utiliza el waitForAsync función de utilidad importada de @angular/core/testing.

Consulte la sección waitForAsync para obtener más detalles.

Reducir la configuración

Solo las últimas tres líneas de este archivo realmente prueban el componente y todo lo que hacen es afirmar que Angular puede crear el componente.

El resto del archivo es un código de configuración estándar que anticipa pruebas más avanzadas que podría se vuelven necesarios si el componente se convierte en algo sustancial.

Aprenderá sobre estas funciones de prueba avanzadas a continuación. Por ahora, puede reducir radicalmente este archivo de prueba a un tamaño más manejable:

describe('BannerComponent (minimal)',()=>it('should create',()=>
    TestBed.configureTestingModule(declarations:[BannerComponent]);const fixture = TestBed.createComponent(BannerComponent);const component = fixture.componentInstance;expect(component).toBeDefined();););

En este ejemplo, el objeto de metadatos pasado a TestBed.configureTestingModule simplemente declara BannerComponent, el componente a probar.

TestBed.configureTestingModule(declarations:[BannerComponent]);

No es necesario declarar ni importar nada más. El módulo de prueba predeterminado está preconfigurado con algo como el BrowserModule de @angular/platform-browser.

Más tarde llamarás TestBed.configureTestingModule() con importaciones, proveedores y más declaraciones para satisfacer sus necesidades de prueba. Opcional override Los métodos pueden afinar aún más aspectos de la configuración.

createComponent()

Después de configurar TestBed, tu lo llamas createComponent() método.

const fixture = TestBed.createComponent(BannerComponent);

TestBed.createComponent() crea una instancia del BannerComponent, agrega un elemento correspondiente al DOM del ejecutor de pruebas y devuelve un ComponentFixture.

No reconfigure TestBed después de llamar createComponent.

los createComponent El método congela la corriente TestBed definición, cerrándola a más configuraciones.

No puedes llamar más TestBed métodos de configuración, no configureTestingModule(), ni get(), ni ninguno de los override... métodos. Si intentas, TestBed arroja un error.

ComponentFixture

ComponentFixture es un arnés de prueba para interactuar con el componente creado y su elemento correspondiente.

Acceda a la instancia del componente a través del accesorio y confirme que existe con una expectativa de Jasmine:

const component = fixture.componentInstance;expect(component).toBeDefined();

beforeEach()

Agregará más pruebas a medida que este componente evolucione. En lugar de duplicar el TestBed configuración para cada prueba, refactoriza para llevar la configuración a un Jasmine beforeEach() y algunas variables de apoyo:

describe('BannerComponent (with beforeEach)',()=>let component: BannerComponent;let fixture: ComponentFixture<BannerComponent>;beforeEach(()=>
    TestBed.configureTestingModule(declarations:[BannerComponent]);
    fixture = TestBed.createComponent(BannerComponent);
    component = fixture.componentInstance;);it('should create',()=>expect(component).toBeDefined();););

Ahora agregue una prueba que obtenga el elemento del componente de fixture.nativeElement y busca el texto esperado.

it('should contain "banner works!"',()=>const bannerElement: HTMLElement = fixture.nativeElement;expect(bannerElement.textContent).toContain('banner works!'););

nativeElement

El valor de ComponentFixture.nativeElement tiene el any escribe. Más tarde encontrarás el DebugElement.nativeElement y también tiene el any escribe.

Angular no puede saber en el momento de la compilación qué tipo de elemento HTML nativeElement es o si incluso es un elemento HTML. Es posible que la aplicación se esté ejecutando en un plataforma sin navegador, como el servidor o un Trabajador web, donde el elemento puede tener una API disminuida o no existir en absoluto.

Las pruebas de esta guía están diseñadas para ejecutarse en un navegador, por lo que nativeElement El valor siempre será un HTMLElement o una de sus clases derivadas.

Sabiendo que es un HTMLElement de algún tipo, puede utilizar el código HTML estándar querySelector para sumergirse más profundamente en el árbol de elementos.

Aquí hay otra prueba que llama HTMLElement.querySelector para obtener el elemento de párrafo y buscar el texto del banner:

it('should have 

with "banner works!"',()=>const bannerElement: HTMLElement = fixture.nativeElement;const p = bannerElement.querySelector('p');expect(p.textContent).toEqual('banner works!'););

DebugElement

El Angular accesorio proporciona el elemento del componente directamente a través del fixture.nativeElement.

const bannerElement: HTMLElement = fixture.nativeElement;

Este es en realidad un método de conveniencia, implementado como fixture.debugElement.nativeElement.

const bannerDe: DebugElement = fixture.debugElement;const bannerEl: HTMLElement = bannerDe.nativeElement;

Hay una buena razón para este camino tortuoso hacia el elemento.

Las propiedades del nativeElement dependen del entorno de ejecución. Podrías estar ejecutando estas pruebas en un no navegador plataforma que no tiene un DOM o cuya emulación de DOM no es compatible con el HTMLElement API.

Angular se basa en DebugElement abstracción para trabajar de forma segura en todas las plataformas compatibles. En lugar de crear un árbol de elementos HTML, Angular crea un DebugElement árbol que envuelve el elementos nativos para la plataforma en tiempo de ejecución. los nativeElement propiedad desenvuelve el DebugElement y devuelve el objeto de elemento específico de la plataforma.

Debido a que las pruebas de muestra de esta guía están diseñadas para ejecutarse solo en un navegador, nativeElement en estas pruebas es siempre un HTMLElement cuyos métodos y propiedades familiares puede explorar dentro de una prueba.

Aquí está la prueba anterior, reimplementada con fixture.debugElement.nativeElement:

it('should find the 

with fixture.debugElement.nativeElement)',()=>const bannerDe: DebugElement = fixture.debugElement;const bannerEl: HTMLElement = bannerDe.nativeElement;const p = bannerEl.querySelector('p');expect(p.textContent).toEqual('banner works!'););

los DebugElement tiene otros métodos y propiedades que son útiles en las pruebas, como verá en otra parte de esta guía.

Importa el DebugElement símbolo de la biblioteca del núcleo angular.

import DebugElement from'@angular/core';

By.css()

Aunque todas las pruebas de esta guía se ejecutan en el navegador, algunas aplicaciones pueden ejecutarse en una plataforma diferente al menos algunas veces.

Por ejemplo, el componente podría procesarse primero en el servidor como parte de una estrategia para hacer que la aplicación se inicie más rápido en dispositivos mal conectados. Es posible que el renderizador del lado del servidor no admita la API de elementos HTML completa. Si no es compatible querySelector, la prueba anterior podría fallar.

los DebugElement ofrece métodos de consulta que funcionan para todas las plataformas compatibles. Estos métodos de consulta toman un predicado función que regresa true cuando un nodo en el DebugElement árbol coincide con los criterios de selección.

Tu creas un predicado con la ayuda de un By clase importada de una biblioteca para la plataforma en tiempo de ejecución. Aquí esta la By importar para el navegador plataforma:

import By from'@angular/platform-browser';

El siguiente ejemplo vuelve a implementar la prueba anterior con DebugElement.query() y el navegador By.css método.

it('should find the 

with fixture.debugElement.query(By.css)',()=>const bannerDe: DebugElement = fixture.debugElement;const paragraphDe = bannerDe.query(By.css('p'));const p: HTMLElement = paragraphDe.nativeElement;expect(p.textContent).toEqual('banner works!'););

Algunas observaciones dignas de mención:

Cuando está filtrando por el selector de CSS y solo probando las propiedades de un navegador elemento nativo, los By.css El enfoque puede ser exagerado.

A menudo es más fácil y claro filtrar con un estándar. HTMLElement método como querySelector() o querySelectorAll().