Test Angular routing without a router spy

— 6 minute read

I've had some unit tests that used a Jasmine spy to spy on the navigate function of Angular Router.

But I wanted to change the test to really check navigation had occured to a specific route defined in the routing configuration of the application. So I had to change some things:

  • Use RouterTestingModule.withRoutes() to provide the app routes to TestBed.
  • Inject the Router and Location service in a test and adapt it.
  • Detect the changes within the fixture and wait for it to be stable.

So I went from this configuration:

const routerSpy = { navigate: jasmine.createSpy('navigate') };

describe('component: AppComponent', function () {
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [
RouterTestingModule,
],
providers: [
{ provide: Router, useValue: routerSpy },
],
declarations: [AppComponent],
}).compileComponents();
});

beforeEach(() => {
fixture = TestBed.createComponent(AppComponent);
component = fixture.debugElement.componentInstance;
fixture.detectChanges();
});

it('should be able to navigate to My Awesome Route', () => {
const customEvent = new CustomEvent('click', {
detail: {
value: { route: 'my-awesome-route' }
}
});
component.itemClicked(customEvent);
expect(routerSpy.navigate).toHaveBeenCalled();
});
});

To this configuration:

describe('component: AppComponent', function () {
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [
RouterTestingModule.withRoutes(routes),
],
declarations: [AppComponent],
}).compileComponents();
});

beforeEach(() => {
fixture = TestBed.createComponent(AppComponent);
component = fixture.debugElement.componentInstance;
fixture.detectChanges();
});

it('should be able to route to My Awesome Route', async(
inject([Router, Location], (router: Router, location: Location) => {
const customEvent = new CustomEvent('click', {
detail: {
value: { route: 'my-awesome-route' }
}
});
component.itemClicked(customEvent);
fixture.detectChanges();
fixture.whenStable().then(() => {
expect(location.path()).toEqual('/my-awesome-route');
});
})
));
});

P.S. The custom event is an event that gets emitted by a child component to let the parent (this component and any type of framework) know to which route to navigate.

P.P.S. In Angular 11+ async should be changed to waitForAsync.