Testing navigateByUrl in Angular Router

If you are building any non-trivial Angular application, there will come a time when you want to enable navigation to another part of the application. Inside a component, this is commonly done using the navigateByUrl function which is part of Router.

When it comes to writing tests, it can get a little bit tricky as to how one might verify that navigateByUrl is being called with the correct arguments when a click event occurs. In this article, I’ll show you one approach to testing this important feature.

Code Under Test

The following code is to be under test, starting with the markup:

<ion-item detail="true" class="details-link" (click)="goToDetails(birthday.uuid)">

Then, in the component logic:

export class BirthdayItemComponent implements OnInit {
    @Input() birthday: Birthday;
    constructor(private router: Router) { }

    ngOnInit(): void {
    }

    goToDetails(uuid: string) {
        this.router.navigateByUrl(`/birthdays/${uuid}`);
    }
}

Mock

As you can see in the component, we will need to ensure we have a uuid variable, which is coming from the instance of Birthday.

Let’s start there and add a Birthday instance below the component set up in the beforeEach:

fixture = TestBed.createComponent(BirthdayItemComponent);
component = fixture.componentInstance;
component.birthday = new Birthday({ uuid: '123', firstName: 'Tester', lastName: 'McGee', birthday: 'January 1'});

Next thing we want to do is start with a mock of the Router:

class RouterStub {
  navigateByUrl(url: string) { return url; }
}

This is a very simple stub, but covers our navigateByUrl method. Next we want to provide it in place of the Router dependency:

await TestBed.configureTestingModule({
  declarations: [ BirthdayItemComponent ],
  providers: [ { provide: Router, useClass: RouterStub } ]
})
.compileComponents();

Now that we are providing the stub, let’s work on our action - clicking the router link in the template.

de = fixture.debugElement.query(By.css('.details-link'));
el = de.nativeElement;

Here we are querying by CSS using the class we assigned to the markup seen above. This gives us the element.

Now, with set up in place, time to write an actual test!

describe('goToDetails', () => {
    it('should navigate to the details page', () => {
        const router = TestBed.get(Router);
        const spy = spyOn(router, 'navigateByUrl');
        el.click();
        const navArgs = spy.calls.first().args[0];
        expect(navArgs).toBe('/birthdays/123');
    });
});

As you can see, we spy on the navigateByUrl method from the mocked instance of router we have set up. Next, we use the handle to complete a click event. Finally, we check the first call made to the spy and see what arguments were passed.

If all goes well, it should be /birthdays/123. The 123 coming from component.birthday.

Let’s run our tests and see:

Jasmine tests

And there you have it. One approach to testing an important part of Angular routing.


Erik August Johnson is a software developer working with JavaScript to build useful things. Follow them on Twitter.