Testing a component depending on  ActivatedRoute's queryParamMap object

Testing a component depending on ActivatedRoute's queryParamMap object

Use jasmine spy and the RouterTestingModule

In this post, we will create a component that uses Angular's routing module and performs some work on the querystring passed to the route. To keep this post straight to the point, I will focus on the ActivatedRoute's snapshop object.

Here is the sample component:

import { Component, OnInit } from '@angular/core';
import {ActivatedRoute} from '@angular/router';

@Component({
  selector: 'app-home',
  templateUrl: './home.component.html',
  styleUrls: ['./home.component.scss']
})
export class HomeComponent implements OnInit {
  value: string;
  constructor(private route: ActivatedRoute) { }

  ngOnInit(): void {
    this.value = this.route.snapshot.queryParamMap.get('name');
  }
}

Notice that we injected ActivatedRoute into this component, which indicates that our app module imported the RouterModule.

How do we test this?

Similar to my previous post on testing services that use HttpClient, the angular team has provided us with a special module to test routing and it's called the RouterTestingModule. Once we import this module, we can ask our TestBed to give us an instance of the ActivatedRoute.

Below is the code:

import {ComponentFixture, TestBed, waitForAsync} from '@angular/core/testing';
import { HomeComponent } from './home.component';
import {ActivatedRoute, Router} from '@angular/router';
import {RouterTestingModule} from '@angular/router/testing';

describe('HomeComponent', () => {
  let component: HomeComponent;
  let fixture: ComponentFixture<HomeComponent>;
  let router: Router;
  let route: ActivatedRoute;
  beforeEach(waitForAsync(() => {
     TestBed.configureTestingModule({
      declarations: [ HomeComponent ],
      imports: [RouterTestingModule.withRoutes([])]
    })
    .compileComponents().then(() => {
        fixture = TestBed.createComponent(HomeComponent);
        component = fixture.componentInstance;
      });
     router = TestBed.inject(Router);
     route = TestBed.inject(ActivatedRoute);
  }));
});

Notice that we are getting an instance of the ActivatedRoute object, but we have not told it what to return.

Lucky for us, jasmine gives us the spyOn function. This will let us spy on the members of our router object and return a value of our liking. Here is our spec:

  it('should create', () => {
    const queryData = 'Toronto'
    const spyRoute = spyOn(route.snapshot.queryParamMap, 'get');
    spyRoute.and.returnValue(queryData);
    fixture.detectChanges();
    expect(component).toBeTruthy();
    expect(component.value).toEqual(queryData);
  });

The spyOn function returns us a jasmine spy object that we then tell to return 'Toronto' string whenever our code calls for the snapshot.queryParamMap.get. It does not matter what key is queried by our component (our HomeComponent sends in the 'name' key in this example), the returned value will always be 'Toronto'.

Note that the fixture.detectChanges() call is very important here. If you skip this line, you will get an error that the component.value was undefined. We have to tell our fixture to run change detection so it can read our querystring and set it's value to our component's value variable.