💻 Exemplos: Capítulo 16: Testes Automatizados com TestBed, ComponentFixture e Vitest
Especialização em Front-End com Angular • Demonstrações Práticas Compiláveis
💻 Código de Exemplo 16 — Testes Unitários de Componentes Standalone com TestBed e Vitest
Este exemplo apresenta a criação de uma suíte de testes unitários para componentes Angular 18 baseados em Signals utilizando TestBed, ComponentFixture e o executor moderno Vitest, validando renderização de inputs, simulação de eventos e asserções no DOM.
1. Componente Alvo: src/app/components/quota-badge.component.ts
// src/app/components/quota-badge.component.ts
import { Component, input, output, computed } from '@angular/core';
@Component({
selector: 'app-quota-badge',
standalone: true,
template: `
<div
class="badge-shell flex items-center justify-between p-3 border rounded-xl"
[class.border-red-500]="isExceeded()"
data-testid="quota-container"
>
<div class="space-y-0.5">
<span class="text-xs text-slate-500" data-testid="quota-label">{{ label() }}</span>
<div class="text-sm font-bold text-slate-900" data-testid="quota-display">
{{ current() }} / {{ limit() }} MB
</div>
</div>
<button
type="button"
(click)="onResetClicked()"
class="text-xs px-2.5 py-1 bg-slate-100 hover:bg-slate-200 text-slate-700 rounded-md font-semibold"
data-testid="btn-reset"
>
Zerar
</button>
</div>
`
})
export class QuotaBadgeComponent {
label = input.required<string>();
current = input<number>(0);
limit = input<number>(100);
reset = output<void>();
isExceeded = computed(() => this.current() >= this.limit());
onResetClicked(): void {
this.reset.emit();
}
}
2. Arquivo de Teste Automatizado: src/app/components/quota-badge.component.spec.ts
// src/app/components/quota-badge.component.spec.ts
import { describe, it, expect, beforeEach } from 'vitest';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ComponentRef } from '@angular/core';
import { QuotaBadgeComponent } from './quota-badge.component';
describe('QuotaBadgeComponent (Angular 18 + Vitest)', () => {
let fixture: ComponentFixture<QuotaBadgeComponent>;
let component: QuotaBadgeComponent;
let componentRef: ComponentRef<QuotaBadgeComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [QuotaBadgeComponent]
}).compileComponents();
fixture = TestBed.createComponent(QuotaBadgeComponent);
component = fixture.componentInstance;
componentRef = fixture.componentRef;
});
it('deve renderizar o label e a quota atual corretamente via signal input', () => {
// Definindo inputs tipadas através do setInput() do ComponentRef
componentRef.setInput('label', 'Memória Redis');
componentRef.setInput('current', 45);
componentRef.setInput('limit', 100);
// Dispara detecção de mudanças para atualizar o DOM
fixture.detectChanges();
const compiled = fixture.nativeElement as HTMLElement;
const labelEl = compiled.querySelector('[data-testid="quota-label"]');
const displayEl = compiled.querySelector('[data-testid="quota-display"]');
expect(labelEl?.textContent?.trim()).toBe('Memória Redis');
expect(displayEl?.textContent?.trim()).toBe('45 / 100 MB');
});
it('deve aplicar borda vermelha quando current() for maior ou igual ao limit()', () => {
componentRef.setInput('label', 'Disco SAS');
componentRef.setInput('current', 120);
componentRef.setInput('limit', 100);
fixture.detectChanges();
const container = fixture.nativeElement.querySelector('[data-testid="quota-container"]') as HTMLElement;
expect(container.classList.contains('border-red-500')).toBe(true);
});
it('deve emitir o signal output "reset" ao clicar no botão Zerar', () => {
componentRef.setInput('label', 'CPU Pool');
let wasResetEmitted = false;
// Subscrevendo ao signal output funcional
component.reset.subscribe(() => {
wasResetEmitted = true;
});
fixture.detectChanges();
const button = fixture.nativeElement.querySelector('[data-testid="btn-reset"]') as HTMLButtonElement;
button.click();
expect(wasResetEmitted).toBe(true);
});
});
🖥️ Saída / Comportamento Esperado
Execução do Vitest no terminal (npx vitest run):
✓ src/app/components/quota-badge.component.spec.ts (3 tests) 94ms
✓ QuotaBadgeComponent (Angular 18 + Vitest) (3)
✓ deve renderizar o label e a quota atual corretamente via signal input
✓ deve aplicar borda vermelha quando current() for maior ou igual ao limit()
✓ deve emitir o signal output "reset" ao clicar no botão Zerar
Test Files 1 passed (1)
Tests 3 passed (3)
Duration 380ms
🔍 Destaques da Implementação
componentRef.setInput(): API oficial para atribuir valores a Signal Inputs em tempo de teste unitário.- Subscrição Simples em Signal Output:
component.reset.subscribe()permite validar a emissão de outputs com clareza. - Seletores Desacoplados (
data-testid): Garante resiliência de testes contra alterações de estilização Tailwind.