Issue
I’ve written Angular tests before, but I’m at a loss on how to create a test that covers the code for the following. I’ll include what I’ve wired up, but the tests are basic.
Here is the service file:
@Injectable()
export class HealthViewService {
constructor(private readonly healthService: HealthService, private router: Router){}
createNewPatient() {
this.healthService.currentStatus = this.healthService.getInitialStatus();
this.router.navigate('health/patient');
}
}
And here’s the spec file:
import {HealthViewService} from '<not showing pathway>';
import {HealthService} from '<not showing pathway again>';
const healthViewService = {
createNewPatient: () => {}
}
const healthServiceStub = { currentStatus: () { return StatusTarget.Inbound; }}
let routerStub = { navigate: () => {} }
describe('HealthViewService', () => {
beforeEach(() => {
TestBed.configureTestingModule({
providers: [{provide: Router, useValue: routerStub },
{provide: HealthService, useValue: healthServiceStub },
{provide: HealthViewService, useValue: healthViewService}]
})
});
describe('createNewPatient', () => {
it('it should route to patient page', () => {
expect(healthViewService.createNewPatient).toBeTruthy();
});
});
});
The good news is this test passes, but it’s not really a good test for code coverage. My question is: Is there some way that I could properly mock up the router and watch where the router navigates too? And can I spy on the HealthService to check values there as well? Any help would be appreciated.
Solution
Several problems from your code:
- your spec file does not test your actual implementation of the
HealthViewService
class because you provide some kind of fake objectconst healthViewService = { createNewPatient: () => {} }
as instance of the service - you expect
healthViewService.createNewPatient
method to exist, which will always be true (you don’t call the method) - the router navigate method expect an array of string, not a string
Here are the fixes you may consider (I removed the HealthService
part to give you a MWE)
@Injectable()
export class HealthViewService {
constructor(private router: Router) {}
createNewPatient() {
this.router.navigate(["health", "patient"]);
}
}
and the corresponding spec file
import { TestBed } from "@angular/core/testing";
import { Router } from "@angular/router";
import { HealthViewService } from "./health-view-service.service";
describe("HealthViewServiceService", () => {
beforeEach(() => {
TestBed.configureTestingModule({
providers: [
// provide the actual implementation you want to test
// shortcut for {provide: HealthViewService, useClass: HealthViewService}
HealthViewService,
// provide a mocked Router, injected through inversion of control mechanism offered by angular
{ provide: Router, useValue: { navigate: () => {} } },
],
});
});
it("createNewPatient() should navigate to health/patient", () => {
const service = TestBed.inject(HealthViewService);
const router = TestBed.inject(Router);
const spy = spyOn(router, "navigate");
service.createNewPatient();
expect(spy).toHaveBeenCalledWith(["health", "patient"]);
});
});