9
votes

I am having some trouble injecting a mock for a RouteParams dependency in a test for an Angular2 component. My general thought is that I might be missing some providers.

The tests fail with:

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

Does anyone know what the issue might be?

import {
  it,
  inject,
  injectAsync,
  describe,
  beforeEach,
  beforeEachProviders,
  TestComponentBuilder
} from 'angular2/testing';

import {Component, provide} from 'angular2/core';
import {BaseRequestOptions, Http} from 'angular2/http';
import {MockBackend} from 'angular2/http/testing';
import {RouteParams, ROUTER_PROVIDERS, ROUTER_PRIMARY_COMPONENT} from 'angular2/router';

// Load the implementations that should be tested
import {Home} from './home';
import {Title} from './providers/title';

describe('Home', () => {
  // provide our implementations or mocks to the dependency injector

  beforeEachProviders(() => [
    Title,
    Home,
    provide(RouteParams, { useValue: new RouteParams({ id: '1' }) }),
    BaseRequestOptions,
    MockBackend,
    provide(Http, {
        useFactory: function(backend, defaultOptions) {
            return new Http(backend, defaultOptions);
        },
        deps: [MockBackend, BaseRequestOptions]
    }),
    provide(RouteParams, {
        useFactory: function() {
            return new RouteParams({ 'id':'1' });
        }
    })
  ]);

  it('should have a title', inject([ Home ], (home) => {
    expect(home.title.value).toEqual('Angular 2');
  }));

  it('should have a http', inject([ Home ], (home) => {
    expect(!!home.http).toEqual(true);
  }));

  it('should log ngOnInit', inject([ Home ], (home) => {
    spyOn(console, 'log');
    spyOn(console, 'info');
    expect(console.log).not.toHaveBeenCalled();
    expect(console.info).not.toHaveBeenCalled();

    home.ngOnInit();
    expect(console.log).toHaveBeenCalled();
    expect(console.info).toHaveBeenCalledWith('1');
  }));

});
2

2 Answers

1
votes

For people who land here even though they are working with Angular4

I just went ahead and created a mock. I guess the trick is to mock the parts of the ActivatedRoute you need. for me this did it:

import {ActivatedRoute, ParamMap} from '@angular/router';

/**
 * Mocking the ActivatedRoute which is injected in the Component on creation.
 * This allows for easier testing as values can be set as needed.
 */
class MockActivatedRoute {
   paramMap = Observable.of(new Params());
}

/**
 * Bare bones implementation of ParamMap used in mock. Further tests can expand
 * on this implementation as needed.
 */
class Params implements ParamMap {
  keys: string[];

  private routes: {[key: string]: string|null} = {
    subject: 'foo',
    time: 'd-123-1',
    device: 'all',
    location: 'c-123'
  };

  constructor() {
    this.keys = Object.keys(this.routes);
  }

  has(name: string): boolean {
    throw new Error('Method not implemented.');
  }
  get(name: string): string|null {
    return this.routes[name];
  }
  getAll(name: string): string[] {
    throw new Error('Method not implemented.');
  }
}

And then make sure in your testing module you provide the mocked service over the real ActivatedRoute:

providers: [
  {
    provide: ActivatedRoute,
    useValue: new MockActivatedRoute(),
  }
]

For completion, the way I use it in the Component I'm testing:

ngOnInit() {
  this.route.paramMap
      .map((params: ParamMap) => params.get('subject') as string)
      .subscribe((subject: string) => this.subject = subject);
}
9
votes

Managed to solve this myself, you mock RouteProvider by using

provide(RouteParams, { useValue: new RouteParams({ id: '1' }) })
import {
  it,
  inject,
  injectAsync,
  describe,
  beforeEach,
  beforeEachProviders,
  TestComponentBuilder
} from 'angular2/testing';

import {Component, provide} from 'angular2/core';
import {BaseRequestOptions, Http} from 'angular2/http';
import {MockBackend} from 'angular2/http/testing';
import {RouteParams, ROUTER_PROVIDERS, ROUTER_PRIMARY_COMPONENT} from 'angular2/router';

// Load the implementations that should be tested
import {Home} from './home';
import {Title} from './providers/title';

describe('Home', () => {
  // provide our implementations or mocks to the dependency injector

  beforeEachProviders(() => [
    Title,
    Home,
    provide(RouteParams, { useValue: new RouteParams({ id: '1' }) }),
    BaseRequestOptions,
    MockBackend,
    provide(Http, {
        useFactory: function(backend, defaultOptions) {
            return new Http(backend, defaultOptions);
        },
        deps: [MockBackend, BaseRequestOptions]
    })
  ]);

  it('should have a title', inject([ Home ], (home) => {
    expect(home.title.value).toEqual('Angular 2');
  }));

  it('should have a http', inject([ Home ], (home) => {
    expect(!!home.http).toEqual(true);
  }));

  it('should log ngOnInit', inject([ Home ], (home) => {
    spyOn(console, 'log');
    spyOn(console, 'info');
    expect(console.log).not.toHaveBeenCalled();
    expect(console.info).not.toHaveBeenCalled();

    home.ngOnInit();
    expect(console.log).toHaveBeenCalled();
    expect(console.info).toHaveBeenCalledWith('1');
  }));

});