Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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. The service has Http injected into it, which I am not interested in testing so I am trying to mock the service and spy on the service's method call. This is something I was quite familiar with doing in Angular 1 but I just can't get to work in Angular 2. The error I'm getting is No provider for Http! I am interested in spying on the actual services method rather than mocking it out.

My component looks like this:

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 looks like this

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([]));
}
like image 461
JoWinchester Avatar asked Oct 21 '16 08:10

JoWinchester


1 Answers

Thanks for your help everyone, it got me looking in the right area. What was missing was importing HttpModule. Big thanks to the Ahmed for suggesting 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 mock objects or anything, perfect

like image 137
JoWinchester Avatar answered Sep 21 '22 12:09

JoWinchester