How To Mock Out Child Components In Unit Tests of Angular 2 Code
I was struggling with this one for a bit but found a
solution this past weekend. When writing unit tests of a component that
contains child components, the solution is to do the following:
1.
Create small mock classes in your test file to
represent the child components and include just the functionality you’ll be
mocking. These classes should have the @Component() decorator applied to them
and contain selectors that match the real components they are being substituted
for.
2.
Specify these mock components in the
declarations section of the object you’re passing to
TestBed.configureTestingModule().
Example:
ActivityMgmtComponent has a few child components. Here are
two of them:
<activity-edit #viewEditActivity></activity-edit>
<activity-history #viewActivityHistory></activity-history>
Notice the # values – these are there to support the
@ViewChild decorator function. This allows the parent component to call
functions on the child components.
@ViewChild('viewActivityHistory') activityHistoryComponent: ActivityHistoryComponent;
@ViewChild('viewEditActivity') activityEditComponent: ActivityEditComponent;
And here’s an example of the parent component doing just
that:
editActivity(): void {
if (this._selectedActivity != null) {
this.activityEditComponent.getActivity(this._selectedActivity.Id);
}
}
In our test file, we create small, simple mocks to
substitute for the real child components:
@Component({
selector: 'activity-history',
template: ''
})
export class ActivityHistoryMock {
getActivityHistory = jasmine.createSpy('getActivityHistory').and.returnValue(null);
}
@Component({
selector: 'activity-edit',
template: ''
})
export class ActivityEditMock {
getActivity = jasmine.createSpy('getActivity').and.returnValue(null);
}
They have the same selectors as the real child components
(important! More on that in a bit), and we’re using Jasmine to create spies on
the methods we’re mocking out.
And we wire them up in our TestBed configuration:
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [
ActivityMgmtComponent,
ActivityDeleteComponent,
ActivityHistoryMock,
ActivityEditMock
],
When the parent component’s template is compiled, it sees
the selectors for the real child components, but associates them with the mocks
– they have the same selectors. We don’t declare the real child components, so
the parent component doesn’t know about them during the test – for all it
knows, these are the real deal.
And here’s one of the tests, using the mock child component
in place of the real one:
it("should get activity on edit", () => {
component.ngOnInit();
component._selectedActivity = component._activities[0];
component._selectedActivity.Id = "352ccb86-6898-4750-b9d4-f141f107c2d6";
component.editActivity();
expect(component.activityEditComponent.getActivity).toHaveBeenCalledWith(component._activities[0].Id);
});
Comments
Post a Comment