How do you inject an angular2 service into a unit test? (RC3)
Asked Answered
V

1

5

I am using RC3. I am implementing the new Angular2 router as documented here: https://angular.io/docs/ts/latest/guide/router.html

Everything works fine but I am having problem in unit testing. Specifically, I cannot inject Angular2 services into my unit tests.

My relevant component code is:

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

@Component({
  templateUrl: ...
  styleUrls: ...
})

export class Route1DetailComponent {

  constructor(private route:ActivatedRoute) {
    console.log(route);
  }
}

my unit test looks like:

import {
  expect, it, iit, xit,
  describe, ddescribe, xdescribe,
  beforeEach, beforeEachProviders, withProviders,
  async, inject
} from '@angular/core/testing';

import {ActivatedRoute} from '@angular/router';
import {Route1DetailComponent} from './route1-detail.component';
import {TestComponentBuilder} from '@angular/compiler/testing';

describe('route1-detail.component.ts', () => {

  beforeEachProviders(() => [
    {provide: ActivatedRoute, useClass: ActivatedRoute}
  ]);

  it('should instantiate component',
    async(inject([TestComponentBuilder, ActivatedRoute], (tcb:TestComponentBuilder, ar: ActivatedRoute) => {
      tcb.createAsync(Route1DetailComponent).then((fixture) => {
        expect(fixture.componentInstance instanceof Route1DetailComponent).toBe(true, 'should create Route1DetailComponent');
        console.log(ar);
    });
  })));
});

The 'should instantiate component' unit test fails. The error is:

Cannot resolve all parameters for 'ActivatedRoute'(?, ?, ?, ?, ?). Make sure that all the parameters are decorated with Inject or have valid type annotations and that 'ActivatedRoute' is decorated with Injectable.

How do I get this working?

When I do not inject ActivatedRoute everything works fine.

Thanks.

Violaviolable answered 28/6, 2016 at 15:38 Comment(2)
github.com/angular/angular/blob/… might helpJuarez
much appreciated still working on this very useful thx - more ideas welcome :)Violaviolable
K
9

When unit testing, sometimes a certain service causes issues just because it's not being used in a normal environment. You can test to see if it has been called, without having the unit test run through the whole service. Do this by creating a mock class.

describe('route1-detail.component.ts', () => {

class MockActivatedRoute {}

beforeEachProviders(() => [
    {provide: ActivatedRoute, useClass: MockActivatedRoute}
  ]);

it('should instantiate component',
  async(inject([TestComponentBuilder, ActivatedRoute], (tcb:TestComponentBuilder, ar: MockActivatedRoute) => {
  tcb.createAsync(Route1DetailComponent).then((fixture) => {
    expect(fixture.componentInstance instanceof Route1DetailComponent).toBe(true, 'should create Route1DetailComponent');
    console.log(ar);
  });
})));

Notice this part: inject([TestComponentBuilder, ActivatedRoute], (tcb:TestComponentBuilder, ar: MockActivatedRoute. When the code is looking for ActivatedRoute, you are passing it the mock service. Of course if you are specifically trying to unit test ActivatedRoute itself, then creating a mock service defeats that purpose. You might have to add methods or variables to the mock class if it tries to call methods from that service.

Koehler answered 28/6, 2016 at 16:36 Comment(3)
Glad to help @ViolaviolableKoehler
This is great! How would you modify the MockActivatedRoute if you were using route paramters?Scoundrelly
I have not tested this but you could try something like this so you can call a fake params from the mock route. It gets more complicated if you're also subscribing to params but I'm sure it could be done. class MockActivatedRoute { params(){} }Koehler

© 2022 - 2024 — McMap. All rights reserved.