Después de de nuestra larga recopilación de información resolvimos este asunto que presentan ciertos los usuarios. Te ofrecemos la respuesta y nuestro objetivo es servirte de gran apoyo.
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:
- Cree un componente con la nueva palabra clave.
- Empuje su API.
- 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 elwaitForAsync
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. Opcionaloverride
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 llamarcreateComponent
.los
createComponent
El método congela la corrienteTestBed
definición, cerrándola a más configuraciones.No puedes llamar más
TestBed
métodos de configuración, noconfigureTestingModule()
, niget()
, ni ninguno de losoverride...
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 havewith "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 thewith 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 thewith 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:
- los
By.css()
el método estático seleccionaDebugElement
nodos con un selector de CSS estándar. - La consulta devuelve un
DebugElement
para el párrafo. - Debes desenvolver ese resultado para obtener el elemento de párrafo.
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()
.
Reseñas y calificaciones de la guía
No se te olvide mostrar esta reseña si te fue de ayuda.