使用 DI 单元测试和模拟服务

jag*_*ipa 5 unit-testing dependency-injection karma-jasmine angular

我已经为此苦苦挣扎了一段时间,我希望有人可以提供帮助。我有一个使用服务获取数据的组件。我正在尝试向它添加单元测试。我的问题是测试总是失败,并显示“错误:没有 Http 提供者”。这是我的代码:

服务:

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;
        });
    }
}
Run Code Online (Sandbox Code Playgroud)

成分:

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;
        });
    }
Run Code Online (Sandbox Code Playgroud)

}

测试:

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));
    })
});
Run Code Online (Sandbox Code Playgroud)

sno*_*ete 3

让我们试试这个:

首先,将提供程序数组从组件移动到 NgModule。最好在模块级别提供服务,因为它将您的提供程序与组件树结构解耦(除非您特别希望每个组件都有一个提供程序的单独实例,并且从简化的用例来看,不需要每个组件都有单独的实例)。

所以,

@Component({
    selector: 'app-contacts',
    templateUrl: './contacts.component.html',
    styleUrls: ['./contacts.component.css'],
   /// providers: [ContactsService]  <-- remove this line
})
export class ContactsComponent implements OnInit {
   .....
Run Code Online (Sandbox Code Playgroud)

并将其添加到声明您的 ContactsComponent 的 NgModule 中

 @NgModule({
    imports: ..
    declarations: ...
    providers: [ContactsService] // <-- provider definition moved to here
 })
 export class ModuleDeclaringContactsComponent
Run Code Online (Sandbox Code Playgroud)

一旦你这样做了,那么在你的测试中模拟 ContactsService 就很容易实现。

TestBed.configureTestingModule({
        imports: [FormsModule],
        declarations: [ContactsComponent],
        providers: [{ provide: ContactsService, useClass: MockContactsService }] // this is needed for the service mock
    });
Run Code Online (Sandbox Code Playgroud)

这样,您就可以开始了。