Angular supports the decomposition of components by allowing you to include one into another. To test a component that contains other elements you have to declare them in the test configuration. Don’t forget about any @Input
property of an aggregated component or running the test suite will result with the following error:
Template parse errors: Can't bind to property since it isn't a known property of component
The components
Let’s use the following two components from the official Angular documentation.
The first one is the HeroChildComponent
that receives a hero
instance from the parent component and displays it in a header:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
// component-interaction/src/app/hero-child.component.ts import { Component, Input } from '@angular/core'; import { Hero } from './hero'; @Component({ selector: 'app-hero-child', template: ` <h3>{{hero.name}} says:</h3> ` }) export class HeroChildComponent { @Input() hero: Hero; } |
The second one is the HeroParentComponent
that passes the hero
instance to the <app-hero-child>
element:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
// component-interaction/src/app/hero-parent.component.ts import { Component } from '@angular/core'; import { HEROES } from './hero'; @Component({ selector: 'app-hero-parent', template: ` <h2>{{master}} controls {{heroes.length}} heroes</h2> <app-hero-child *ngFor="let hero of heroes" [hero]="hero" </app-hero-child> ` }) export class HeroParentComponent { heroes = HEROES; } |
Test the parent component with input binding
The test for the HeroParentComponent
will succeed if we declare in it the child component – including the property that is passed with @Input
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
// component-interaction/src/app/hero-parent.component.spec.ts … import { Component, Input} from '@angular/core'; import { Hero } from './hero'; @Component({selector: 'app-hero-child', template: ''}) class HeroChildComponent { @Input() hero: Hero; } … declarations: [ HeroParentComponent, HeroChildComponent ] … |
The following file contains the full test file for HeroParentComponent
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
// component-interaction/src/app/hero-parent.component.spec.ts import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { Component, Input} from '@angular/core'; import { HeroParentComponent } from './app-hero-parent.component'; import { Hero } from './hero'; @Component({selector: 'app-hero-child', template: ''}) class HeroChildComponent { @Input() hero: Hero; } describe('HeroParentComponent', () => { let component: HeroParentComponent; let fixture: ComponentFixture<HeroParentComponent>; beforeEach(async(() => { TestBed.configureTestingModule({ declarations: [ HeroParentComponent, HeroChildComponent ] }) .compileComponents(); })); beforeEach(() => { fixture = TestBed.createComponent(HeroParentComponent); component = fixture.componentInstance; fixture.detectChanges(); }); it('should create', () => { expect(component).toBeTruthy(); }); }); |
Test the child component
Without mocking the hero
property in the child component, the test suite fails with the following error:
HeroChildComponent should create
[object ErrorEvent] thrown
You can simply create the mock for hero
like this:
1 2 3 4 5 6 7 8 |
// component-interaction/src/app/hero-child.component.spec.ts … import { Hero } from './hero'; … beforeEach(() => { … component.hero = {id:1, name: 'hero1'}; … |
The following file contains the full test file for HeroChildComponent
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { HeroChildComponent } from './app-hero-child.component'; import { Hero } from './hero'; describe('HeroChildComponent', () => { let component: HeroChildComponent; let fixture: ComponentFixture<HeroChildComponent>; beforeEach(async(() => { TestBed.configureTestingModule({ declarations: [ HeroChildComponent ] }) .compileComponents(); })); beforeEach(() => { fixture = TestBed.createComponent(HeroChildComponent); component = fixture.componentInstance; component.hero = {id:1, name: 'hero1'}; fixture.detectChanges(); }); it('should create', () => { expect(component).toBeTruthy(); }); }); |
Photo by Daria Shevtsova on StockSnap