Bien que les réponses aux votes les plus élevés fonctionnent, elles ne démontrent pas de bonnes pratiques de test, j'ai donc pensé développer la réponse de Günter avec quelques exemples pratiques.
Imaginons que nous ayons le composant simple suivant:
@Component({
selector: 'my-demo',
template: `
<button (click)="buttonClicked()">Click Me!</button>
`
})
export class DemoComponent {
@Output() clicked = new EventEmitter<string>();
constructor() { }
buttonClicked(): void {
this.clicked.emit('clicked!');
}
}
Le composant est le système testé, espionner certaines de ses parties casse l'encapsulation. Les tests de composants angulaires ne devraient connaître que trois choses:
- Le DOM (accessible via par exemple
fixture.nativeElement.querySelector
);
- Noms des
@Input
s et@Output
s; et
- Services collaboratifs (injectés via le système DI).
Tout ce qui implique l'invocation directe de méthodes sur l'instance ou l'espionnage de parties du composant est trop étroitement lié à l'implémentation, et ajoutera du frottement au refactoring - les doubles de test ne devraient être utilisés que pour les collaborateurs. Dans ce cas, comme nous n'avons pas de collaborateurs, nous ne devrions pas avoir besoin de simulacres, d'espions ou d'autres doublons de test.
Une façon de tester ceci est de s'abonner directement à l'émetteur, puis d'appeler l'action de clic (voir Composant avec entrées et sorties ):
describe('DemoComponent', () => {
let component: DemoComponent;
let fixture: ComponentFixture<DemoComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ DemoComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(DemoComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should emit when clicked', () => {
let emitted: string;
component.clicked.subscribe((event: string) => {
emitted = event;
});
fixture.nativeElement.querySelector('button').click();
expect(emitted).toBe('clicked!');
});
});
Bien que cela interagisse directement avec l'instance de composant, le nom de @Output
fait partie de l'API publique, il n'est donc pas trop étroitement couplé.
Vous pouvez également créer un hôte de test simple (voir Composant dans un hôte de test ) et monter réellement votre composant:
@Component({
selector: 'test-host',
template: `
<my-demo (clicked)="onClicked($event)"></my-demo>
`
})
class TestHostComponent {
lastClick = '';
onClicked(value: string): void {
this.lastClick = value;
}
}
puis testez le composant en contexte:
describe('DemoComponent', () => {
let component: TestHostComponent;
let fixture: ComponentFixture<TestHostComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ TestHostComponent, DemoComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(TestHostComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should emit when clicked', () => {
fixture.nativeElement.querySelector('button').click();
expect(component.lastClick).toBe('clicked!');
});
});
Le componentInstance
ici est l' hôte de test , donc nous pouvons être sûrs que nous ne sommes pas trop Coupled pour la composante nous fait tester.