Um component, diferentemente de todas as outras partes de uma aplicação Angular, combina um template HTML e uma classe TypeScript. O component é verdadeiramente o template e a classe trabalhando juntos. Para testar adequadamente um component, você deve testar que eles funcionam juntos conforme pretendido.
Tais testes exigem criar o elemento host do component no DOM do browser, assim como o Angular faz, e investigar a interação da classe do component com o DOM conforme descrito pelo seu template.
O TestBed do Angular facilita esse tipo de teste, como você verá nas seções seguintes.
Mas em muitos casos, testar apenas a classe do component, sem envolvimento do DOM, pode validar muito do comportamento do component de forma direta e mais óbvia.
Testes DOM de component
Um component é mais do que apenas sua classe. Um component interage com o DOM e com outros components. Classes sozinhas não podem dizer se o component vai renderizar corretamente, responder a entradas e gestos do usuário, ou integrar com seus components pai e filho.
- O
Lightswitch.clicked()está vinculado a algo de modo que o usuário possa invocá-lo? - A
Lightswitch.messageé exibida? - O usuário pode realmente selecionar o hero exibido por
DashboardHeroComponent? - O nome do hero é exibido conforme esperado (como em maiúsculas)?
- A mensagem de boas-vindas é exibida pelo template de
WelcomeComponent?
Estas podem não ser questões problemáticas para os components simples precedentes ilustrados. Mas muitos components têm interações complexas com os elementos DOM descritos em seus templates, fazendo com que HTML apareça e desapareça conforme o estado do component muda.
Para responder a esses tipos de perguntas, você precisa criar os elementos DOM associados aos components, deve examinar o DOM para confirmar que o estado do component é exibido corretamente nos momentos apropriados, e deve simular a interação do usuário com a tela para determinar se essas interações fazem o component se comportar conforme esperado.
Para escrever esses tipos de teste, você usará recursos adicionais do TestBed, bem como outros helpers de teste.
Testes gerados pela CLI
A CLI cria um arquivo de teste inicial para você por padrão quando você pede para gerar um novo component.
Por exemplo, o seguinte comando da CLI gera um BannerComponent na pasta app/banner (com template e estilos inline):
ng generate component banner --inline-template --inline-style --module app
Ele também gera um arquivo de teste inicial para o component, banner-external.component.spec.ts, que se parece com isto:
app/banner/banner-external.component.spec.ts (inicial)
import {DebugElement} from '@angular/core';import {ComponentFixture, TestBed, waitForAsync} from '@angular/core/testing';import {By} from '@angular/platform-browser';import {BannerComponent} from './banner-initial.component';/*import { BannerComponent } from './banner.component';describe('BannerComponent', () => {*/describe('BannerComponent (initial CLI generated)', () => { let component: BannerComponent; let fixture: ComponentFixture<BannerComponent>; beforeEach(waitForAsync(() => { TestBed.configureTestingModule({imports: [BannerComponent]}); })); beforeEach(() => { fixture = TestBed.createComponent(BannerComponent); component = fixture.componentInstance; fixture.detectChanges(); }); it('should create', () => { expect(component).toBeDefined(); });});describe('BannerComponent (minimal)', () => { it('should create', () => { TestBed.configureTestingModule({imports: [BannerComponent]}); const fixture = TestBed.createComponent(BannerComponent); const component = fixture.componentInstance; expect(component).toBeDefined(); });});describe('BannerComponent (with beforeEach)', () => { let component: BannerComponent; let fixture: ComponentFixture<BannerComponent>; beforeEach(() => { TestBed.configureTestingModule({imports: [BannerComponent]}); fixture = TestBed.createComponent(BannerComponent); component = fixture.componentInstance; }); it('should create', () => { expect(component).toBeDefined(); }); it('should contain "banner works!"', () => { const bannerElement: HTMLElement = fixture.nativeElement; expect(bannerElement.textContent).toContain('banner works!'); }); it('should have <p> with "banner works!"', () => { const bannerElement: HTMLElement = fixture.nativeElement; const p = bannerElement.querySelector('p')!; expect(p.textContent).toEqual('banner works!'); }); it('should find the <p> 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!'); }); it('should find the <p> 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!'); });});
ÚTIL: Como compileComponents é assíncrono, ele usa a função utilitária waitForAsync importada de @angular/core/testing.
Consulte a seção waitForAsync para mais detalhes.
Reduzir a configuração
Apenas as últimas três linhas deste arquivo realmente testam o component e tudo o que fazem é afirmar que o Angular pode criar o component.
O resto do arquivo é código de configuração boilerplate antecipando testes mais avançados que podem se tornar necessários se o component evoluir para algo substancial.
Você aprenderá sobre esses recursos avançados de teste nas seções seguintes. Por enquanto, você pode reduzir radicalmente este arquivo de teste para um tamanho mais gerenciável:
app/banner/banner-initial.component.spec.ts (mínimo)
import {DebugElement} from '@angular/core';import {ComponentFixture, TestBed, waitForAsync} from '@angular/core/testing';import {By} from '@angular/platform-browser';import {BannerComponent} from './banner-initial.component';/*import { BannerComponent } from './banner.component';describe('BannerComponent', () => {*/describe('BannerComponent (initial CLI generated)', () => { let component: BannerComponent; let fixture: ComponentFixture<BannerComponent>; beforeEach(waitForAsync(() => { TestBed.configureTestingModule({imports: [BannerComponent]}); })); beforeEach(() => { fixture = TestBed.createComponent(BannerComponent); component = fixture.componentInstance; fixture.detectChanges(); }); it('should create', () => { expect(component).toBeDefined(); });});describe('BannerComponent (minimal)', () => { it('should create', () => { TestBed.configureTestingModule({imports: [BannerComponent]}); const fixture = TestBed.createComponent(BannerComponent); const component = fixture.componentInstance; expect(component).toBeDefined(); });});describe('BannerComponent (with beforeEach)', () => { let component: BannerComponent; let fixture: ComponentFixture<BannerComponent>; beforeEach(() => { TestBed.configureTestingModule({imports: [BannerComponent]}); fixture = TestBed.createComponent(BannerComponent); component = fixture.componentInstance; }); it('should create', () => { expect(component).toBeDefined(); }); it('should contain "banner works!"', () => { const bannerElement: HTMLElement = fixture.nativeElement; expect(bannerElement.textContent).toContain('banner works!'); }); it('should have <p> with "banner works!"', () => { const bannerElement: HTMLElement = fixture.nativeElement; const p = bannerElement.querySelector('p')!; expect(p.textContent).toEqual('banner works!'); }); it('should find the <p> 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!'); }); it('should find the <p> 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!'); });});
Neste exemplo, o objeto de metadata passado para TestBed.configureTestingModule simplesmente declara BannerComponent, o component a ser testado.
import {DebugElement} from '@angular/core';import {ComponentFixture, TestBed, waitForAsync} from '@angular/core/testing';import {By} from '@angular/platform-browser';import {BannerComponent} from './banner-initial.component';/*import { BannerComponent } from './banner.component';describe('BannerComponent', () => {*/describe('BannerComponent (initial CLI generated)', () => { let component: BannerComponent; let fixture: ComponentFixture<BannerComponent>; beforeEach(waitForAsync(() => { TestBed.configureTestingModule({imports: [BannerComponent]}); })); beforeEach(() => { fixture = TestBed.createComponent(BannerComponent); component = fixture.componentInstance; fixture.detectChanges(); }); it('should create', () => { expect(component).toBeDefined(); });});describe('BannerComponent (minimal)', () => { it('should create', () => { TestBed.configureTestingModule({imports: [BannerComponent]}); const fixture = TestBed.createComponent(BannerComponent); const component = fixture.componentInstance; expect(component).toBeDefined(); });});describe('BannerComponent (with beforeEach)', () => { let component: BannerComponent; let fixture: ComponentFixture<BannerComponent>; beforeEach(() => { TestBed.configureTestingModule({imports: [BannerComponent]}); fixture = TestBed.createComponent(BannerComponent); component = fixture.componentInstance; }); it('should create', () => { expect(component).toBeDefined(); }); it('should contain "banner works!"', () => { const bannerElement: HTMLElement = fixture.nativeElement; expect(bannerElement.textContent).toContain('banner works!'); }); it('should have <p> with "banner works!"', () => { const bannerElement: HTMLElement = fixture.nativeElement; const p = bannerElement.querySelector('p')!; expect(p.textContent).toEqual('banner works!'); }); it('should find the <p> 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!'); }); it('should find the <p> 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!'); });});
ÚTIL: Não há necessidade de declarar ou importar mais nada.
O module de teste padrão é pré-configurado com algo como o BrowserModule de @angular/platform-browser.
Mais tarde você chamará TestBed.configureTestingModule() com imports, providers e mais declarações para atender às suas necessidades de teste.
Métodos override opcionais podem ajustar ainda mais aspectos da configuração.
createComponent()
Após configurar o TestBed, você chama seu método createComponent().
import {DebugElement} from '@angular/core';import {ComponentFixture, TestBed, waitForAsync} from '@angular/core/testing';import {By} from '@angular/platform-browser';import {BannerComponent} from './banner-initial.component';/*import { BannerComponent } from './banner.component';describe('BannerComponent', () => {*/describe('BannerComponent (initial CLI generated)', () => { let component: BannerComponent; let fixture: ComponentFixture<BannerComponent>; beforeEach(waitForAsync(() => { TestBed.configureTestingModule({imports: [BannerComponent]}); })); beforeEach(() => { fixture = TestBed.createComponent(BannerComponent); component = fixture.componentInstance; fixture.detectChanges(); }); it('should create', () => { expect(component).toBeDefined(); });});describe('BannerComponent (minimal)', () => { it('should create', () => { TestBed.configureTestingModule({imports: [BannerComponent]}); const fixture = TestBed.createComponent(BannerComponent); const component = fixture.componentInstance; expect(component).toBeDefined(); });});describe('BannerComponent (with beforeEach)', () => { let component: BannerComponent; let fixture: ComponentFixture<BannerComponent>; beforeEach(() => { TestBed.configureTestingModule({imports: [BannerComponent]}); fixture = TestBed.createComponent(BannerComponent); component = fixture.componentInstance; }); it('should create', () => { expect(component).toBeDefined(); }); it('should contain "banner works!"', () => { const bannerElement: HTMLElement = fixture.nativeElement; expect(bannerElement.textContent).toContain('banner works!'); }); it('should have <p> with "banner works!"', () => { const bannerElement: HTMLElement = fixture.nativeElement; const p = bannerElement.querySelector('p')!; expect(p.textContent).toEqual('banner works!'); }); it('should find the <p> 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!'); }); it('should find the <p> 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!'); });});
TestBed.createComponent() cria uma instância do BannerComponent, adiciona um elemento correspondente ao DOM do test-runner e retorna um ComponentFixture.
IMPORTANTE: Não reconfigure o TestBed após chamar createComponent.
O método createComponent congela a definição atual do TestBed, fechando-o para configuração adicional.
Você não pode chamar mais nenhum método de configuração do TestBed, nem configureTestingModule(), nem get(), nem qualquer um dos métodos override....
Se você tentar, o TestBed lança um erro.
ComponentFixture
O ComponentFixture é um test harness para interagir com o component criado e seu elemento correspondente.
Acesse a instância do component através do fixture e confirme que ela existe com uma expectation do Jasmine:
import {DebugElement} from '@angular/core';import {ComponentFixture, TestBed, waitForAsync} from '@angular/core/testing';import {By} from '@angular/platform-browser';import {BannerComponent} from './banner-initial.component';/*import { BannerComponent } from './banner.component';describe('BannerComponent', () => {*/describe('BannerComponent (initial CLI generated)', () => { let component: BannerComponent; let fixture: ComponentFixture<BannerComponent>; beforeEach(waitForAsync(() => { TestBed.configureTestingModule({imports: [BannerComponent]}); })); beforeEach(() => { fixture = TestBed.createComponent(BannerComponent); component = fixture.componentInstance; fixture.detectChanges(); }); it('should create', () => { expect(component).toBeDefined(); });});describe('BannerComponent (minimal)', () => { it('should create', () => { TestBed.configureTestingModule({imports: [BannerComponent]}); const fixture = TestBed.createComponent(BannerComponent); const component = fixture.componentInstance; expect(component).toBeDefined(); });});describe('BannerComponent (with beforeEach)', () => { let component: BannerComponent; let fixture: ComponentFixture<BannerComponent>; beforeEach(() => { TestBed.configureTestingModule({imports: [BannerComponent]}); fixture = TestBed.createComponent(BannerComponent); component = fixture.componentInstance; }); it('should create', () => { expect(component).toBeDefined(); }); it('should contain "banner works!"', () => { const bannerElement: HTMLElement = fixture.nativeElement; expect(bannerElement.textContent).toContain('banner works!'); }); it('should have <p> with "banner works!"', () => { const bannerElement: HTMLElement = fixture.nativeElement; const p = bannerElement.querySelector('p')!; expect(p.textContent).toEqual('banner works!'); }); it('should find the <p> 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!'); }); it('should find the <p> 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!'); });});
beforeEach()
Você adicionará mais testes conforme este component evolui.
Em vez de duplicar a configuração do TestBed para cada teste, você refatora para colocar a configuração em um beforeEach() do Jasmine e algumas variáveis de suporte:
import {DebugElement} from '@angular/core';import {ComponentFixture, TestBed, waitForAsync} from '@angular/core/testing';import {By} from '@angular/platform-browser';import {BannerComponent} from './banner-initial.component';/*import { BannerComponent } from './banner.component';describe('BannerComponent', () => {*/describe('BannerComponent (initial CLI generated)', () => { let component: BannerComponent; let fixture: ComponentFixture<BannerComponent>; beforeEach(waitForAsync(() => { TestBed.configureTestingModule({imports: [BannerComponent]}); })); beforeEach(() => { fixture = TestBed.createComponent(BannerComponent); component = fixture.componentInstance; fixture.detectChanges(); }); it('should create', () => { expect(component).toBeDefined(); });});describe('BannerComponent (minimal)', () => { it('should create', () => { TestBed.configureTestingModule({imports: [BannerComponent]}); const fixture = TestBed.createComponent(BannerComponent); const component = fixture.componentInstance; expect(component).toBeDefined(); });});describe('BannerComponent (with beforeEach)', () => { let component: BannerComponent; let fixture: ComponentFixture<BannerComponent>; beforeEach(() => { TestBed.configureTestingModule({imports: [BannerComponent]}); fixture = TestBed.createComponent(BannerComponent); component = fixture.componentInstance; }); it('should create', () => { expect(component).toBeDefined(); }); it('should contain "banner works!"', () => { const bannerElement: HTMLElement = fixture.nativeElement; expect(bannerElement.textContent).toContain('banner works!'); }); it('should have <p> with "banner works!"', () => { const bannerElement: HTMLElement = fixture.nativeElement; const p = bannerElement.querySelector('p')!; expect(p.textContent).toEqual('banner works!'); }); it('should find the <p> 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!'); }); it('should find the <p> 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!'); });});
Agora adicione um teste que obtém o elemento do component de fixture.nativeElement e procura pelo texto esperado.
import {DebugElement} from '@angular/core';import {ComponentFixture, TestBed, waitForAsync} from '@angular/core/testing';import {By} from '@angular/platform-browser';import {BannerComponent} from './banner-initial.component';/*import { BannerComponent } from './banner.component';describe('BannerComponent', () => {*/describe('BannerComponent (initial CLI generated)', () => { let component: BannerComponent; let fixture: ComponentFixture<BannerComponent>; beforeEach(waitForAsync(() => { TestBed.configureTestingModule({imports: [BannerComponent]}); })); beforeEach(() => { fixture = TestBed.createComponent(BannerComponent); component = fixture.componentInstance; fixture.detectChanges(); }); it('should create', () => { expect(component).toBeDefined(); });});describe('BannerComponent (minimal)', () => { it('should create', () => { TestBed.configureTestingModule({imports: [BannerComponent]}); const fixture = TestBed.createComponent(BannerComponent); const component = fixture.componentInstance; expect(component).toBeDefined(); });});describe('BannerComponent (with beforeEach)', () => { let component: BannerComponent; let fixture: ComponentFixture<BannerComponent>; beforeEach(() => { TestBed.configureTestingModule({imports: [BannerComponent]}); fixture = TestBed.createComponent(BannerComponent); component = fixture.componentInstance; }); it('should create', () => { expect(component).toBeDefined(); }); it('should contain "banner works!"', () => { const bannerElement: HTMLElement = fixture.nativeElement; expect(bannerElement.textContent).toContain('banner works!'); }); it('should have <p> with "banner works!"', () => { const bannerElement: HTMLElement = fixture.nativeElement; const p = bannerElement.querySelector('p')!; expect(p.textContent).toEqual('banner works!'); }); it('should find the <p> 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!'); }); it('should find the <p> 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!'); });});
nativeElement
O valor de ComponentFixture.nativeElement tem o tipo any.
Mais tarde você encontrará o DebugElement.nativeElement e ele também tem o tipo any.
O Angular não pode saber em tempo de compilação que tipo de elemento HTML o nativeElement é ou se é mesmo um elemento HTML.
A aplicação pode estar sendo executada em uma plataforma não-browser, como o servidor ou um Web Worker, onde o elemento pode ter uma API reduzida ou não existir de modo algum.
Os testes neste guia são projetados para executar em um browser, então um valor nativeElement sempre será um HTMLElement ou uma de suas classes derivadas.
Sabendo que é um HTMLElement de algum tipo, use o querySelector padrão HTML para mergulhar mais fundo na árvore de elementos.
Aqui está outro teste que chama HTMLElement.querySelector para obter o elemento de parágrafo e procurar pelo texto do banner:
import {DebugElement} from '@angular/core';import {ComponentFixture, TestBed, waitForAsync} from '@angular/core/testing';import {By} from '@angular/platform-browser';import {BannerComponent} from './banner-initial.component';/*import { BannerComponent } from './banner.component';describe('BannerComponent', () => {*/describe('BannerComponent (initial CLI generated)', () => { let component: BannerComponent; let fixture: ComponentFixture<BannerComponent>; beforeEach(waitForAsync(() => { TestBed.configureTestingModule({imports: [BannerComponent]}); })); beforeEach(() => { fixture = TestBed.createComponent(BannerComponent); component = fixture.componentInstance; fixture.detectChanges(); }); it('should create', () => { expect(component).toBeDefined(); });});describe('BannerComponent (minimal)', () => { it('should create', () => { TestBed.configureTestingModule({imports: [BannerComponent]}); const fixture = TestBed.createComponent(BannerComponent); const component = fixture.componentInstance; expect(component).toBeDefined(); });});describe('BannerComponent (with beforeEach)', () => { let component: BannerComponent; let fixture: ComponentFixture<BannerComponent>; beforeEach(() => { TestBed.configureTestingModule({imports: [BannerComponent]}); fixture = TestBed.createComponent(BannerComponent); component = fixture.componentInstance; }); it('should create', () => { expect(component).toBeDefined(); }); it('should contain "banner works!"', () => { const bannerElement: HTMLElement = fixture.nativeElement; expect(bannerElement.textContent).toContain('banner works!'); }); it('should have <p> with "banner works!"', () => { const bannerElement: HTMLElement = fixture.nativeElement; const p = bannerElement.querySelector('p')!; expect(p.textContent).toEqual('banner works!'); }); it('should find the <p> 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!'); }); it('should find the <p> 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!'); });});
DebugElement
O fixture do Angular fornece o elemento do component diretamente através de fixture.nativeElement.
import {DebugElement} from '@angular/core';import {ComponentFixture, TestBed, waitForAsync} from '@angular/core/testing';import {By} from '@angular/platform-browser';import {BannerComponent} from './banner-initial.component';/*import { BannerComponent } from './banner.component';describe('BannerComponent', () => {*/describe('BannerComponent (initial CLI generated)', () => { let component: BannerComponent; let fixture: ComponentFixture<BannerComponent>; beforeEach(waitForAsync(() => { TestBed.configureTestingModule({imports: [BannerComponent]}); })); beforeEach(() => { fixture = TestBed.createComponent(BannerComponent); component = fixture.componentInstance; fixture.detectChanges(); }); it('should create', () => { expect(component).toBeDefined(); });});describe('BannerComponent (minimal)', () => { it('should create', () => { TestBed.configureTestingModule({imports: [BannerComponent]}); const fixture = TestBed.createComponent(BannerComponent); const component = fixture.componentInstance; expect(component).toBeDefined(); });});describe('BannerComponent (with beforeEach)', () => { let component: BannerComponent; let fixture: ComponentFixture<BannerComponent>; beforeEach(() => { TestBed.configureTestingModule({imports: [BannerComponent]}); fixture = TestBed.createComponent(BannerComponent); component = fixture.componentInstance; }); it('should create', () => { expect(component).toBeDefined(); }); it('should contain "banner works!"', () => { const bannerElement: HTMLElement = fixture.nativeElement; expect(bannerElement.textContent).toContain('banner works!'); }); it('should have <p> with "banner works!"', () => { const bannerElement: HTMLElement = fixture.nativeElement; const p = bannerElement.querySelector('p')!; expect(p.textContent).toEqual('banner works!'); }); it('should find the <p> 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!'); }); it('should find the <p> 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!'); });});
Isto é na verdade um método de conveniência, implementado como fixture.debugElement.nativeElement.
import {DebugElement} from '@angular/core';import {ComponentFixture, TestBed, waitForAsync} from '@angular/core/testing';import {By} from '@angular/platform-browser';import {BannerComponent} from './banner-initial.component';/*import { BannerComponent } from './banner.component';describe('BannerComponent', () => {*/describe('BannerComponent (initial CLI generated)', () => { let component: BannerComponent; let fixture: ComponentFixture<BannerComponent>; beforeEach(waitForAsync(() => { TestBed.configureTestingModule({imports: [BannerComponent]}); })); beforeEach(() => { fixture = TestBed.createComponent(BannerComponent); component = fixture.componentInstance; fixture.detectChanges(); }); it('should create', () => { expect(component).toBeDefined(); });});describe('BannerComponent (minimal)', () => { it('should create', () => { TestBed.configureTestingModule({imports: [BannerComponent]}); const fixture = TestBed.createComponent(BannerComponent); const component = fixture.componentInstance; expect(component).toBeDefined(); });});describe('BannerComponent (with beforeEach)', () => { let component: BannerComponent; let fixture: ComponentFixture<BannerComponent>; beforeEach(() => { TestBed.configureTestingModule({imports: [BannerComponent]}); fixture = TestBed.createComponent(BannerComponent); component = fixture.componentInstance; }); it('should create', () => { expect(component).toBeDefined(); }); it('should contain "banner works!"', () => { const bannerElement: HTMLElement = fixture.nativeElement; expect(bannerElement.textContent).toContain('banner works!'); }); it('should have <p> with "banner works!"', () => { const bannerElement: HTMLElement = fixture.nativeElement; const p = bannerElement.querySelector('p')!; expect(p.textContent).toEqual('banner works!'); }); it('should find the <p> 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!'); }); it('should find the <p> 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!'); });});
Há uma boa razão para este caminho tortuoso até o elemento.
As propriedades do nativeElement dependem do ambiente de runtime.
Você pode estar executando esses testes em uma plataforma não-browser que não tem um DOM ou cuja emulação de DOM não suporta a API completa do HTMLElement.
O Angular depende da abstração DebugElement para funcionar com segurança em todas as plataformas suportadas.
Em vez de criar uma árvore de elementos HTML, o Angular cria uma árvore DebugElement que envolve os elementos nativos para a plataforma de runtime.
A propriedade nativeElement desempacota o DebugElement e retorna o objeto de elemento específico da plataforma.
Como os testes de exemplo deste guia são projetados para executar apenas em um browser, um nativeElement nesses testes é sempre um HTMLElement cujos métodos e propriedades familiares você pode explorar dentro de um teste.
Aqui está o teste anterior, reimplementado com fixture.debugElement.nativeElement:
import {DebugElement} from '@angular/core';import {ComponentFixture, TestBed, waitForAsync} from '@angular/core/testing';import {By} from '@angular/platform-browser';import {BannerComponent} from './banner-initial.component';/*import { BannerComponent } from './banner.component';describe('BannerComponent', () => {*/describe('BannerComponent (initial CLI generated)', () => { let component: BannerComponent; let fixture: ComponentFixture<BannerComponent>; beforeEach(waitForAsync(() => { TestBed.configureTestingModule({imports: [BannerComponent]}); })); beforeEach(() => { fixture = TestBed.createComponent(BannerComponent); component = fixture.componentInstance; fixture.detectChanges(); }); it('should create', () => { expect(component).toBeDefined(); });});describe('BannerComponent (minimal)', () => { it('should create', () => { TestBed.configureTestingModule({imports: [BannerComponent]}); const fixture = TestBed.createComponent(BannerComponent); const component = fixture.componentInstance; expect(component).toBeDefined(); });});describe('BannerComponent (with beforeEach)', () => { let component: BannerComponent; let fixture: ComponentFixture<BannerComponent>; beforeEach(() => { TestBed.configureTestingModule({imports: [BannerComponent]}); fixture = TestBed.createComponent(BannerComponent); component = fixture.componentInstance; }); it('should create', () => { expect(component).toBeDefined(); }); it('should contain "banner works!"', () => { const bannerElement: HTMLElement = fixture.nativeElement; expect(bannerElement.textContent).toContain('banner works!'); }); it('should have <p> with "banner works!"', () => { const bannerElement: HTMLElement = fixture.nativeElement; const p = bannerElement.querySelector('p')!; expect(p.textContent).toEqual('banner works!'); }); it('should find the <p> 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!'); }); it('should find the <p> 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!'); });});
O DebugElement tem outros métodos e propriedades que são úteis em testes, como você verá em outros lugares neste guia.
Você importa o símbolo DebugElement da biblioteca core do Angular.
import {DebugElement} from '@angular/core';import {ComponentFixture, TestBed, waitForAsync} from '@angular/core/testing';import {By} from '@angular/platform-browser';import {BannerComponent} from './banner-initial.component';/*import { BannerComponent } from './banner.component';describe('BannerComponent', () => {*/describe('BannerComponent (initial CLI generated)', () => { let component: BannerComponent; let fixture: ComponentFixture<BannerComponent>; beforeEach(waitForAsync(() => { TestBed.configureTestingModule({imports: [BannerComponent]}); })); beforeEach(() => { fixture = TestBed.createComponent(BannerComponent); component = fixture.componentInstance; fixture.detectChanges(); }); it('should create', () => { expect(component).toBeDefined(); });});describe('BannerComponent (minimal)', () => { it('should create', () => { TestBed.configureTestingModule({imports: [BannerComponent]}); const fixture = TestBed.createComponent(BannerComponent); const component = fixture.componentInstance; expect(component).toBeDefined(); });});describe('BannerComponent (with beforeEach)', () => { let component: BannerComponent; let fixture: ComponentFixture<BannerComponent>; beforeEach(() => { TestBed.configureTestingModule({imports: [BannerComponent]}); fixture = TestBed.createComponent(BannerComponent); component = fixture.componentInstance; }); it('should create', () => { expect(component).toBeDefined(); }); it('should contain "banner works!"', () => { const bannerElement: HTMLElement = fixture.nativeElement; expect(bannerElement.textContent).toContain('banner works!'); }); it('should have <p> with "banner works!"', () => { const bannerElement: HTMLElement = fixture.nativeElement; const p = bannerElement.querySelector('p')!; expect(p.textContent).toEqual('banner works!'); }); it('should find the <p> 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!'); }); it('should find the <p> 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!'); });});
By.css()
Embora os testes neste guia sejam todos executados no browser, algumas aplicações podem executar em uma plataforma diferente pelo menos parte do tempo.
Por exemplo, o component pode renderizar primeiro no servidor como parte de uma estratégia para fazer a aplicação iniciar mais rápido em dispositivos com conexão ruim.
O renderer server-side pode não suportar a API completa de elementos HTML.
Se ele não suportar querySelector, o teste anterior poderia falhar.
O DebugElement oferece métodos de query que funcionam para todas as plataformas suportadas.
Esses métodos de query recebem uma função predicate que retorna true quando um nó na árvore DebugElement corresponde aos critérios de seleção.
Você cria um predicate com a ajuda de uma classe By importada de uma biblioteca para a plataforma de runtime.
Aqui está o import By para a plataforma browser:
import {DebugElement} from '@angular/core';import {ComponentFixture, TestBed, waitForAsync} from '@angular/core/testing';import {By} from '@angular/platform-browser';import {BannerComponent} from './banner-initial.component';/*import { BannerComponent } from './banner.component';describe('BannerComponent', () => {*/describe('BannerComponent (initial CLI generated)', () => { let component: BannerComponent; let fixture: ComponentFixture<BannerComponent>; beforeEach(waitForAsync(() => { TestBed.configureTestingModule({imports: [BannerComponent]}); })); beforeEach(() => { fixture = TestBed.createComponent(BannerComponent); component = fixture.componentInstance; fixture.detectChanges(); }); it('should create', () => { expect(component).toBeDefined(); });});describe('BannerComponent (minimal)', () => { it('should create', () => { TestBed.configureTestingModule({imports: [BannerComponent]}); const fixture = TestBed.createComponent(BannerComponent); const component = fixture.componentInstance; expect(component).toBeDefined(); });});describe('BannerComponent (with beforeEach)', () => { let component: BannerComponent; let fixture: ComponentFixture<BannerComponent>; beforeEach(() => { TestBed.configureTestingModule({imports: [BannerComponent]}); fixture = TestBed.createComponent(BannerComponent); component = fixture.componentInstance; }); it('should create', () => { expect(component).toBeDefined(); }); it('should contain "banner works!"', () => { const bannerElement: HTMLElement = fixture.nativeElement; expect(bannerElement.textContent).toContain('banner works!'); }); it('should have <p> with "banner works!"', () => { const bannerElement: HTMLElement = fixture.nativeElement; const p = bannerElement.querySelector('p')!; expect(p.textContent).toEqual('banner works!'); }); it('should find the <p> 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!'); }); it('should find the <p> 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!'); });});
O exemplo seguinte reimplementa o teste anterior com DebugElement.query() e o método By.css do browser.
import {DebugElement} from '@angular/core';import {ComponentFixture, TestBed, waitForAsync} from '@angular/core/testing';import {By} from '@angular/platform-browser';import {BannerComponent} from './banner-initial.component';/*import { BannerComponent } from './banner.component';describe('BannerComponent', () => {*/describe('BannerComponent (initial CLI generated)', () => { let component: BannerComponent; let fixture: ComponentFixture<BannerComponent>; beforeEach(waitForAsync(() => { TestBed.configureTestingModule({imports: [BannerComponent]}); })); beforeEach(() => { fixture = TestBed.createComponent(BannerComponent); component = fixture.componentInstance; fixture.detectChanges(); }); it('should create', () => { expect(component).toBeDefined(); });});describe('BannerComponent (minimal)', () => { it('should create', () => { TestBed.configureTestingModule({imports: [BannerComponent]}); const fixture = TestBed.createComponent(BannerComponent); const component = fixture.componentInstance; expect(component).toBeDefined(); });});describe('BannerComponent (with beforeEach)', () => { let component: BannerComponent; let fixture: ComponentFixture<BannerComponent>; beforeEach(() => { TestBed.configureTestingModule({imports: [BannerComponent]}); fixture = TestBed.createComponent(BannerComponent); component = fixture.componentInstance; }); it('should create', () => { expect(component).toBeDefined(); }); it('should contain "banner works!"', () => { const bannerElement: HTMLElement = fixture.nativeElement; expect(bannerElement.textContent).toContain('banner works!'); }); it('should have <p> with "banner works!"', () => { const bannerElement: HTMLElement = fixture.nativeElement; const p = bannerElement.querySelector('p')!; expect(p.textContent).toEqual('banner works!'); }); it('should find the <p> 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!'); }); it('should find the <p> 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!'); });});
Algumas observações notáveis:
- O método estático
By.css()seleciona nósDebugElementcom um seletor CSS padrão. - A query retorna um
DebugElementpara o parágrafo. - Você deve desempacotar esse resultado para obter o elemento de parágrafo.
Quando você está filtrando por seletor CSS e testando apenas propriedades de um elemento nativo do browser, a abordagem By.css pode ser excessiva.
Geralmente é mais direto e claro filtrar com um método HTMLElement padrão como querySelector() ou querySelectorAll().