使用DI进行单元测试和服务模拟 [英] Unit testing and mocking a service with DI
问题描述
一段时间以来,我一直在为此苦苦挣扎,希望有人能提供帮助.我有一个使用服务获取数据的组件.我正在尝试向其中添加单元测试.我的问题是测试始终失败,并显示错误:Http没有提供程序".这是我的代码:
I have been struggling with this for a while, and I'm hoping someone can help. I have a component that uses a service to get data. I am attempting to add unit tests to it. My problem is that the tests always fail with "Error: No provider for Http". Here is my code:
服务:
import { Injectable } from '@angular/core';
import { Http } from '@angular/http';
import { Observable } from 'rxjs/Rx';
import 'rxjs/add/operator/map';
import { Contact } from './contact.model';
@Injectable()
export class ContactsService {
constructor(private http: Http) { }
public getContacts(): Observable<Array<Contact>> {
return this.http.get('assets/contacts.json').map(res => {
let r = res.json().Contacts;
return r;
});
}
}
组件:
import { Component, OnInit, NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { Contact } from '../contact.model';
import { ContactsService } from '../contacts.service';
@Component({
selector: 'app-contacts',
templateUrl: './contacts.component.html',
styleUrls: ['./contacts.component.css'],
providers: [ContactsService]
})
export class ContactsComponent implements OnInit {
contactsAll: Array<Contact>;
contacts: Array<Contact>;
constructor(private contactsService: ContactsService) { }
ngOnInit() {
this.contactsService.getContacts().subscribe((x) => {
this.contactsAll = x;
this.contacts = this.contactsAll;
});
}
}
测试:
import { async, ComponentFixture, TestBed, inject } from '@angular/core/testing';
import { FormsModule } from '@angular/forms';
import { By } from '@angular/platform-browser';
import { Observable } from 'rxjs/Rx';
import { ContactsComponent } from './contacts.component';
import { ContactsService } from '../contacts.service';
import { Contact } from '../contact.model';
class MockContactsService extends ContactsService {
constructor() {
super(null);
}
testContacts: Array<Contact> = [
new Contact("test1 mock", 12345, 10000),
new Contact("test2 mock", 23456, 20000)
];
public getContacts(): Observable<Array<Contact>> {
return Observable.of(this.testContacts);
}
}
describe('ContactsComponent', () => {
let component: ContactsComponent;
let fixture: ComponentFixture<ContactsComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [FormsModule],
declarations: [ContactsComponent],
// providers: [{ provide: ContactsService, useClass: MockContactsService }] // this is needed for the service mock
}).overrideComponent(ContactsService, {// The following is to override the provider in the @Component(...) metadata
set: {
providers: [
{ provide: ContactsService, useClass: MockContactsService },
]
}
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(ContactsComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
describe('1st test', () => {
it('true is true', () => expect(true).toBe(true));
})
});
推荐答案
让我们尝试一下:
首先,将providers数组从组件移至NgModule.最好在模块级别提供服务,因为它会使提供程序与组件树结构脱钩(除非您特别希望每个组件具有单独的提供程序实例,并且从简化的用例来看,没有)每个组件都需要一个单独的实例).
First, move your providers array from your component to your NgModule. It's better to provide your services at the module level since it de-couples your providers from your component tree structure (unless you specifically want to have a separate instance of a provider per component, and from your simplified use case, there's no need for a separate instance per component).
所以
@Component({
selector: 'app-contacts',
templateUrl: './contacts.component.html',
styleUrls: ['./contacts.component.css'],
/// providers: [ContactsService] <-- remove this line
})
export class ContactsComponent implements OnInit {
.....
并将其添加到声明您的ContactsComponent的NgModule中
and add it to the NgModule that declares your ContactsComponent
@NgModule({
imports: ..
declarations: ...
providers: [ContactsService] // <-- provider definition moved to here
})
export class ModuleDeclaringContactsComponent
一旦这样做,就可以轻松实现测试中对ContactsService的模拟.
Once you do that, then mocking the ContactsService in your test is easy to implement.
TestBed.configureTestingModule({
imports: [FormsModule],
declarations: [ContactsComponent],
providers: [{ provide: ContactsService, useClass: MockContactsService }] // this is needed for the service mock
});
有了它,你应该很好了.
With that, you should be good to go.
这篇关于使用DI进行单元测试和服务模拟的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!