Angular 2 component testing with Jasmine spies "No provider for Http!" error

I am trying to test an Angular 2 component that uses a service. This service has Http, which is not interesting to me, so I try to mock the service and spy on calling the service method. This is what I knew well with Angular 1, but I just can't work in Angular 2. The error I get is not an Http provider! I am interested in tracking the method of actual services, rather than mocking it.

My component is as follows:

import { Component, OnInit } from '@angular/core'; import { NavBarLink } from '../../models/nav-bar-link'; import { NavBarService } from '../../services/nav-bar/nav-bar.service'; @Component({ selector: 'nav-bar', providers: [NavBarService], moduleId: module.id, templateUrl: 'nav-bar.template.html' }) export class NavBarComponent { constructor(private _navBarService: NavBarService) { } links: NavBarLink[]; getLinks(): void { this._navBarService.getNavBarLinks().then(data => this.links = data); } ngOnInit(): void { this.getLinks(); } } 

And my service looks like this:

 import { Injectable } from '@angular/core'; import { Headers, Http } from '@angular/http'; import 'rxjs/add/operator/toPromise'; import { Urls } from '../../constants/urls.constants'; import { NavBarLink } from '../../models/nav-bar-link'; @Injectable() export class NavBarService { constructor(private _http: Http, private _urls: Urls) { } getNavBarLinks():Promise<NavBarLink[]> { return this._http.get(this._urls.NAV_BAR_LINKS) .toPromise() .then(response => { let navLinks = []; for(let navLink of response.json()) { navLinks.push(new NavBarLink(navLink.id, navLink.description, navLink.route)); } return navLinks; }) .catch(this.handleError); } private handleError(error: any): Promise<any> { console.error('An error occurred', error); // for demo purposes only return Promise.reject(error.message || error); } } 

And finally, my test is as follows

 import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { Provider } from '@angular/core'; import { By } from '@angular/platform-browser'; import { DebugElement } from '@angular/core'; import { NavBarComponent } from './nav-bar.component'; import { NavBarService } from '../../services/nav-bar/nav-bar.service'; import { Observable } from 'rxjs/Rx'; let comp: NavBarComponent; let fixture: ComponentFixture<NavBarComponent>; let navBarService; class MockNavBarService extends NavBarService{ constructor() { super(null, null); } } describe ('NavBarComponent tests', () => { beforeEach( async(() => { TestBed.configureTestingModule({ declarations: [ NavBarComponent ], providers: [ {provide: NavBarService, useClass: MockNavBarService} ] }) .compileComponents() .then(createComponent); })); it('should call the getNavBarLinks when ngOnInit is called', () => { comp.ngOnInit(); expect(navBarService.getNavBarLinks).toHaveBeenCalled(); }); }); function createComponent() { fixture = TestBed.createComponent(NavBarComponent); comp = fixture.componentInstance; navBarService = fixture.debugElement.injector.get(NavBarService); spyOn(navBarService, 'getNavBarLinks').and.returnValue(Promise.resolve([])); } 
+6
source share
3 answers

Thank you for all your help, it made me look in the right area. Failed to import HttpModule. Many thanks to Ahmed for this. Here is my fixed test for reference:

 import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { Provider } from '@angular/core'; import { By } from '@angular/platform-browser'; import { DebugElement } from '@angular/core'; import { HttpModule } from '@angular/http'; // <==== IMPORT THIS import { MockBackend } from '@angular/http/testing'; import { NavBarComponent } from './nav-bar.component'; import { NavBarService } from '../../services/nav-bar/nav-bar.service'; import { Urls } from '../../constants/urls.constants'; import { Observable } from 'rxjs/Rx'; let comp: NavBarComponent; let fixture: ComponentFixture<NavBarComponent>; var navBarService; describe ('NavBarComponent tests', () => { beforeEach( async(() => { TestBed.configureTestingModule({ declarations: [ NavBarComponent ], imports: [ HttpModule], //<==IMPORT INTO TEST BED providers: [ Urls, MockBackend, NavBarService ] }) .compileComponents() .then(createComponent); })); it('should call the getNavBarLinks when ngOnInit is called', () => { comp.ngOnInit(); expect(navBarService.getNavBarLinks).toHaveBeenCalled(); }); }); function createComponent() { fixture = TestBed.createComponent(NavBarComponent); comp = fixture.componentInstance; navBarService = fixture.debugElement.injector.get(NavBarService); spyOn(navBarService, 'getNavBarLinks').and.returnValue(Promise.resolve([])); } 

No need for fake objects or anything like that, great

+6
source

Because of this

 @Component({ providers: [NavBarService], <==== !!!!! }) export class NavBarComponent { 

@Component.providers takes precedence over any provided and module level. Thus, Angular will try to create an instance of NavBarService instead of using the layout that you configured in the test.

Angular allows you to override @Component.providers as well as other things like @Component.template . We can do this with the following

 TestBed.configureTestingModule({ declarations: [NavBarComponent] }) .overrideComponent(NavBarComponent, { set: { providers: [ { provide: NavBarService, useClass: MockNavBarService} ] } }) .compileComponents() .then(createComponent) 
0
source

The problem is that the MockNavBarService extends the NavBarService, so it is expected that Http will be provided. I don’t understand the technical reason why this is so, but here it is. If you remove the inheritance, you can instead implement mock getNavBarLinks() , which returns the Observable of some finished data. Then in your test, you can check what the NavBarComponent does with the data, and not check the fact that some method has been called.

0
source

Source: https://habr.com/ru/post/1011538/


All Articles