unit-testing - Angular 2 在不使用 Testbed 的情况下测试注入(inject) Http 的服务

标签 unit-testing angular karma-runner

我想测试一个简单的 Angular 2 数据服务。该服务使用 Http,但仅此而已。在quickstart guide它说:

However, it's often more productive to explore the inner logic of application classes with isolated unit tests that don't depend upon Angular. Such tests are often smaller and easier to read, write, and maintain.

它给出的编写独立单元测试的示例是,对于简单的服务,您可以通过在每个测试中创建它的新实例来测试服务......可能是这样的:

beforeEach(() => { service = new EventDataService(); });

it('#getEvents should return an observable', () => {
    expect(service.getEvents()).toBe(Observable.from([]);
});

但是,我的 EventDataService 使用 Http,所以如果我不像这样将 Http 放入构造函数中,我会收到错误消息:

beforeEach(() => { service = new EventDataService(http: Http); });

但是 Http 不存在,除非我导入它,我不想这样做 - 我不想测试 Http。我尝试将 http stub ,但我尝试过的所有方法都以失败告终,或者导致我导入更多东西以满足 Typescript 之神......

我确定我想多了。我已经在很多讨论 Angular 2 测试的网站上尝试过这些建议,但是任何超过几个月的东西对我来说都是可疑的,因为框架在过去 6-12 个月里发生了很大变化。对于这样一个简单的示例,我觉得我应该能够保持简单。

我是不是做错了什么? 我正在使用 Angular 2 V 2.4.10、Webpack 2.3.1、Sinon 2.1.0 和 Typescript 2.2.1。

服务:

import { Injectable } from "@angular/core";
import { Http } from "@angular/http";
import { Observable } from "rxjs";
import { Event } from "../event/event.interface";

@Injectable()
export class EventDataService {
    events: Event[];

    constructor(private http: Http) { }

    getEvents(): Observable<Event[]> {
        return this.http.get("api/events")
        .map((response) => {return response.json(); })
    }
};

规范:

import { Http } from "@angular/http";
import { EventDataService } from "./event-data.service";
import * as sinon from "sinon";
import { expect } from "chai";

describe("Event Data Service", () => {
    it("GetEvents", () => {
        sinon.stub(Http, "get").returns(Promise.resolve("sinon Event!"));
        let eventDataService = new EventDataService();

        expect(eventDataService.getEvents()).to.equal("sinon event!");;
    });
});

Thank you!

最佳答案

尽管我全心全意地同意您应该在这种情况下使用 TestBed 来消除 Http 依赖的普遍观点(毕竟,这是 Angular 首先具有依赖注入(inject)的一个巨大动机),但我看到了一些您方法中的错误似乎表明存在一些误解。

您的 EventDataService 有一个构造函数,它需要一个 Http 类型的参数。因此,无论何时您想要手动创建 EventDataService 的实例,都必须使用构造函数并传递一个类型为 Http 的参数。

所以,你应该这样做:

let dataService = new EventDataService(x);

其中 x 是 Http 类型的变量。可能不太明显的是,Typescript 不是像 Java 这样的语言——如果您愿意,Typescript 可以让开。因此,例如,您可以只创建一个新对象,并说它的类型为“Http”,Typescript 编译器会假设您知道自己在做什么并让您继续。

所以你可以这样做:

let x = ({ } as Http);
let dataService = new EventDataService(x);

第一行是告诉编译器我希望 {} 的类型为 Http,Typescript 将让开并假定您知道自己在做什么。

因此,您可以使用该技术获取 Http 的“实例”以进行测试 - 我对“实例”一词的使用非常宽松。

但是,如果您查看 EventDataService,它希望传递给其构造函数的 http 对象具有 get 方法,该方法返回一个您可以调用 map 的对象。换句话说,它期望 get 返回一个 Observable。因此,如果您想出于测试目的伪造 Http,您的伪造 Http 实例需要有一个 get 方法,并且该 get 方法需要返回一个 Observable。

将以上所有内容放在一起,如果我想在不使用 TestBed 的情况下编写自己的测试,我最终会得到:

    let fakeHttp = {
      get: (_: any) => {}
    };

    // I'm not familiar with sinon,
    // but i believe this is stubbing the get method of fakeHttp
    // and returning a canned response
    sinon.stub(fakeHttp, "get").returns(Observable.of("sinon Event!"));

    let eventDataService = new EventDataService(fakeHttp);

    // remember, getEvents returns an observable,
    // so to test it you have to subscribe to it and check its values
    eventDataService.getEvents().subscribe(data => {
      expect(data).to.equal("sinon Event!");
    });

我会这样处理吗?可能不是——我只是使用 TestBed 来获取一个假的 Http 实例(不是因为这种方法有那么困难,而是因为当你有多个依赖项时,TestBed 会简化事情,而且服务似乎总是以这种方式增长)。但归根结底,构造函数依赖注入(inject),就像 Angular 使用的一样,非常容易理解——将你的依赖项(假的或真实的)作为参数传递给你试图为其创建实例的类的构造函数。

关于unit-testing - Angular 2 在不使用 Testbed 的情况下测试注入(inject) Http 的服务,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43057001/

相关文章:

angular - Angular 2+-动态更改应用程序的基本路径

javascript - AngularJS - 如何对操作 HTML 的指令进行单元测试?

angular - 引用错误: describe is not defined Karma jasmine angular2

angular - ngrx 测试方法调度 Action

spring - 如何在 Spring 测试中排除存储库?

Angular 2 : Access property of pipe returning object

selenium - 使用或不使用 headless 浏览器运行 Karma 和 Protractor 测试

java - Mockito:如何在不模拟所有参数的情况下轻松 stub 方法

c# - 如何使用 TestScheduler 完成超时行为?

Angular ngModelChange Internet Explorer 11