How to mock ActivatedRoute in Angular tests
When mocking ActivatedRoute, the goal is usually to provide stub route data
for the component under test.
ActivatedRoute.snapshot
Let's assume we have a TargetComponent which relies on the paramId
from ActivatedRoute.snapshot.
@Component({
selector: 'target',
template: '{{ param }}',
})
class TargetComponent {
public param: string | null = null;
public constructor(private route: ActivatedRoute) {}
ngOnInit() {
this.param = this.route.snapshot.paramMap.get('paramId');
}
}
In the test, we want ActivatedRoute to provide paramValue as paramId.
To do so, we can use MockInstance.
The idea is to mock the snapshot getter and return stub params from it:
it('works with snapshot from ActivatedRoute', () => {
MockInstance(ActivatedRoute, 'snapshot', jasmine.createSpy(), 'get')
.and.returnValue({
paramMap: new Map([['paramId', 'paramValue']]),
});
// in case of jest
// MockInstance(ActivatedRoute, 'snapshot', jest.fn(), 'get')
// .mockReturnValue({
// paramMap: new Map([['paramId', 'paramValue']]),
// });
// the rest of the test
// ...
});
Now any access to ActivatedRoute.snapshot returns a stub paramMap
with the params we need.
ActivatedRoute.params
If a component relies on ActivatedRoute.params, the setup is very similar:
it('works with params from ActivatedRoute', () => {
MockInstance(ActivatedRoute, 'params', jasmine.createSpy(), 'get')
.and.returnValue(of({ paramId: 'paramValue' }));
// in case of jest
// MockInstance(ActivatedRoute, 'params', jest.fn(), 'get')
// .mockReturnValue(of({ paramId: 'paramValue' }));
// the rest of the test
// ...
});
RouterModule.forRoot
If you want to mock a module which imports RouterModule.forRoot,
keep only the component under test:
// TargetModule and RouterModule.forRoot will be mocks
beforeEach(() => MockBuilder(
TargetComponent, // keep
TargetModule, // mock
));
RouterModule.forChild
If you want to mock a module which imports RouterModule.forChild,
you also need to add RouterModule.forRoot to the mocks.
Otherwise, ActivatedRoute and other router dependencies will not be available:
// TargetModule, RouterModule.forChild and RouterModule.forRoot will be mocks
beforeEach(() => MockBuilder(
TargetComponent, // keep
[TargetModule, RouterModule.forRoot([])], // mock, add here RouterModule.forRoot([])
));
Live example how to mock ActivatedRoute
import { Component, NgModule, OnInit } from '@angular/core';
import { ActivatedRoute, RouterModule } from '@angular/router';
import { MockBuilder, MockInstance, MockRender } from 'ng-mocks';
@Component({
selector: 'route',
template: '{{ param }}',
})
class RouteComponent implements OnInit {
public param: string | null = null;
public constructor(private route: ActivatedRoute) {}
ngOnInit() {
this.param = this.route.snapshot.paramMap.get('paramId');
}
}
@NgModule({
declarations: [RouteComponent],
imports: [
RouterModule.forRoot([
{
path: 'test/:paramId',
component: RouteComponent,
},
]),
],
})
class TargetModule {}
describe('MockActivatedRoute', () => {
// Resets customizations after each test, in our case of `ActivatedRoute`.
MockInstance.scope();
// Keeping RouteComponent as it is and mocking all declarations in TargetModule.
beforeEach(() => MockBuilder(RouteComponent, TargetModule));
it('uses paramId from ActivatedRoute', () => {
// Let's set the params of the snapshot.
MockInstance(
ActivatedRoute,
'snapshot',
jasmine.createSpy(),
'get',
).and.returnValue({
paramMap: new Map([['paramId', 'paramValue']]),
});
// in case of jest
// MockInstance(
// ActivatedRoute,
// 'snapshot',
// jest.fn(),
// 'get',
// ).mockReturnValue({
// paramMap: new Map([['paramId', 'paramValue']]),
// });
// Rendering RouteComponent.
const fixture = MockRender(RouteComponent);
// Asserting it got the right paramId.
expect(fixture.point.componentInstance.param).toEqual(
'paramValue',
);
});
});