Reputation: 3848
I have annoying error that probably by my mistake I cannot resolve. I have on simple component which is actually nothing else than a top-bar element in my web application.
This component as you can see has only one dependency, the UserService
and it uses it quite simply:
import { Component, OnInit } from '@angular/core';
import { MdButton } from '@angular2-material/button';
import { MdIcon , MdIconRegistry} from '@angular2-material/icon';
import { UserService } from '../services/index';
import { RouteConfig, ROUTER_DIRECTIVES, Router, ROUTER_PROVIDERS
} from '@angular/router-deprecated';
@Component({
moduleId: module.id,
selector: 'top-bar',
templateUrl: 'top-bar.component.html',
styleUrls: ['top-bar.component.css'],
providers: [MdIconRegistry, UserService, ROUTER_PROVIDERS],
directives: [MdButton, MdIcon, ROUTER_DIRECTIVES]
})
export class TopBarComponent implements OnInit {
constructor(private userService: UserService) {
this.userService = userService;
}
ngOnInit() {
}
/**
* Call UserService and logout() method
*/
logout() {
this.userService.logout();
}
}
As this service has also some dependencies (router etc) I had to provide them at the beforeEachProviders
method as you can see:
import {
beforeEach,
beforeEachProviders,
describe,
expect,
it,
inject,
} from '@angular/core/testing';
import { TopBarComponent } from './top-bar.component';
import {
Router, RootRouter, RouteRegistry, ROUTER_PRIMARY_COMPONENT
} from '@angular/router-deprecated';
import { provide } from '@angular/core';
import { SpyLocation } from '@angular/common/testing';
import { UserService } from '../services/index';
describe('Component: TopBar', () => {
beforeEachProviders(() => [
RouteRegistry,
provide(Location, { useClass: SpyLocation }),
provide(ROUTER_PRIMARY_COMPONENT, { useValue: TopBarComponent }),
provide(Router, { useClass: RootRouter }),
UserService,
TopBarComponent
]);
it('should inject the component', inject([TopBarComponent],
(component: TopBarComponent) => {
expect(component).toBeTruthy();
}));
});
When I run the test though I get this error message:
Chrome 51.0.2704 (Mac OS X 10.11.5) Component: TopBar should inject the component FAILED Error: No provider for Location! (TopBarComponent -> UserService -> Router -> Location) Error: DI Exception[......]
First of all as you can see the Location provider is provided. And secondary, why my test requires to provide (or inject) also the dependencies of the used into the tested component service?
For example if from the above test I remove the Router the even that my component doesn't use Router I'll get an error because the used service does. Then shouldn't I received the same error in the component and not only in the test?
UPDATE - CHANGE OF CODE & ERROR MESSAGE
I have manage to stop getting this error by changing my spec doe to this:
import {
beforeEach,
describe,
expect,
it,
} from '@angular/core/testing';
import { TopBarComponent } from './top-bar.component';
import { UserService } from '../services/index';
import {
Router
} from '@angular/router-deprecated';
import { Http } from '@angular/http';
import { AuthHttp } from 'angular2-jwt';
describe('Component: TopBar', () => {
let router: any = Router;
let authHttp: any = AuthHttp;
let http: any = Http;
let component: TopBarComponent;
let service: UserService = new UserService(router, authHttp, http);
beforeEach(() => {
component = new TopBarComponent(service);
});
it('logout function should work ', () => {
let logout = component.logout;
logout();
expect(localStorage.getItem('token')).toBe(null);
});
});
But know I'm getting this error from my component:
TypeError: Cannot read property 'userService' of undefined
The mentioned error is on this function:
logout() {
this.userService.logout();
}
of my component but this is only on the test. In app it works normally. The function cannot reach constructor's parameter for some reason in my test.
kind of stack here...
Upvotes: 4
Views: 9019
Reputation: 74
By your code I understand that you are trying to test the topbar component.
Top bar component has a dependency on UserService.
So to answer your question, Angular does dependency injection when you run your application because all the providers are configured in the module file. But when you try to test the code in spec file you have to configure the testbed with all providers, components in the beforeEach method that are going to be used and angular leaves all the responsibility of resolving the dependency to the user as testbed acts as environment to run your code.
In your code you can do something like this
let service: UserService;
beforeEach(() => {
TestBed.configureTestingModule({
providers: [UserService, any other service on which user service is dependent] });
});
Here you can see TestBed.configureTestingModule method creates a dummy module to aid in running ur test case.
My suggestion will be create a mock UserService which doesn't have any other dependencies like the original one and assign it in the provider
Something like this
export MockUserService {
Put all essential methods stub here.
}
let service: UserService;
beforeEach(() => {
TestBed.configureTestingModule({
providers: [provide: UserService, useClass: MockUserService] });
});
Then just test the topBar component's usecases.
Upvotes: 3
Reputation: 74
Try creating the object of service inside beforeEach using TestBed.get(UserService). This code will automatically resolve the dependencies and create that object for use.
Remove '= new UserService(router, authHttp, http);' from 'let service: UserService = new UserService(router, authHttp, http);'
Upvotes: 0