Obwohl die Antworten mit den höchsten Stimmen funktionieren, zeigen sie keine guten Testpraktiken. Daher dachte ich, ich würde Günters Antwort mit einigen praktischen Beispielen erweitern.
Stellen wir uns vor, wir haben die folgende einfache Komponente:
@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!');
}
}
Die Komponente ist das zu testende System, das Teile davon ausspioniert und die Kapselung unterbricht. Winkelkomponententests sollten nur drei Dinge wissen:
- Das DOM (Zugriff über zB
fixture.nativeElement.querySelector
);
- Namen der
@Input
s und @Output
s; und
- Collaborating Services (über das DI-System injiziert).
Alles, was das direkte Aufrufen von Methoden für die Instanz oder das Ausspionieren von Teilen der Komponente umfasst, ist zu eng mit der Implementierung verbunden und führt zu Reibungsverlusten beim Refactoring. Test-Doubles sollten nur für die Mitarbeiter verwendet werden. In diesem Fall sollten wir, da wir keine Mitarbeiter haben, keine Verspottungen, Spione oder andere Test-Doubles benötigen .
Eine Möglichkeit, dies zu testen, besteht darin, den Emitter direkt zu abonnieren und dann die Klickaktion aufzurufen (siehe Komponente mit Ein- und Ausgängen ):
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!');
});
});
Obwohl dies direkt mit der Komponenteninstanz interagiert, ist der Name von @Output
Teil der öffentlichen API, sodass er nicht zu eng gekoppelt ist.
Alternativ können Sie einen einfachen Testhost erstellen (siehe Komponente in einem Testhost ) und Ihre Komponente tatsächlich bereitstellen:
@Component({
selector: 'test-host',
template: `
<my-demo (clicked)="onClicked($event)"></my-demo>
`
})
class TestHostComponent {
lastClick = '';
onClicked(value: string): void {
this.lastClick = value;
}
}
Testen Sie dann die Komponente im Kontext:
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!');
});
});
Das componentInstance
hier ist die Test - Host , so dass wir sicher sein können , wir übermäßig nicht an die Komponente gekoppelt sind wir tatsächlich zu testen.