我正在尝试测试使用服务的Angular 2组件。该服务注入了Http,我对测试不感兴趣所以我试图模拟服务并监视服务的方法调用。这是我在Angular 1中非常熟悉的事情,但我无法在Angular 2中工作。我得到的错误是Http的无提供者!我有兴趣监视实际的服务方法,而不是嘲笑它。
我的组件如下所示:
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();
}
}
我的服务看起来像这样:
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);
}
}
最后我的测试看起来像这样
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([]));
}
答案 0 :(得分:6)
感谢大家的帮助,让我看到了正确的区域。缺少的是导入HttpModule。非常感谢艾哈迈德的建议。这是我的固定测试参考:
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([]));
}
不需要模拟对象或任何东西,完美
答案 1 :(得分:0)
这是因为这个
@Component({
providers: [NavBarService], <==== !!!!!
})
export class NavBarComponent {
@Component.providers
优先于所提供的任何内容和模块级别。因此,Angular将尝试创建NavBarService
的实例,而不是使用您在测试中配置的模拟。
Angular允许我们覆盖@Component.providers
以及@Component.template
等其他内容。我们可以使用以下
TestBed.configureTestingModule({
declarations: [NavBarComponent]
})
.overrideComponent(NavBarComponent, {
set: {
providers: [
{ provide: NavBarService, useClass: MockNavBarService}
]
}
})
.compileComponents()
.then(createComponent)
答案 2 :(得分:0)
问题是MockNavBarService扩展了NavBarService,因此仍然希望提供Http。我不明白为什么会出现这种情况的技术原因,但确实如此。如果删除继承,则可以实现模拟getNavBarLinks()
,它返回一些Observable的某些预制数据。然后在测试中,您可以测试NavBarComponent对数据的作用,而不是测试某些方法被调用的事实。