Unit testing Angular 2测试在不使用测试床的情况下注入Http的服务

Unit testing Angular 2测试在不使用测试床的情况下注入Http的服务,unit-testing,angular,karma-runner,Unit Testing,Angular,Karma Runner,我想测试一个简单的Angular 2数据服务。该服务使用Http,但不使用其他内容。在报告中说: 然而,探索企业的内在逻辑往往更有成效 具有独立单元测试的应用程序类,这些测试不依赖于 有棱角的这样的测试通常更小,更容易读、写、写 维持 它给出的编写独立单元测试的示例是,对于简单服务,您可以通过在每个测试中创建一个新的服务实例来测试该服务。。。可能是这样的: beforeEach(() => { service = new EventDataService(); }); it('#getE

我想测试一个简单的Angular 2数据服务。该服务使用Http,但不使用其他内容。在报告中说:

然而,探索企业的内在逻辑往往更有成效 具有独立单元测试的应用程序类,这些测试不依赖于 有棱角的这样的测试通常更小,更容易读、写、写 维持

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

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,但我尝试的所有方法都以失败告终,或者导致我导入更多的东西来满足打字脚本的要求

我肯定我想得太多了。我已经在很多讨论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 { async, getTestBed, TestBed, inject } from "@angular/core/testing";
 import { Response, ResponseOptions, HttpModule, XHRBackend } from "@angular/http";
 import { MockBackend, MockConnection } from "@angular/http/testing";
 import { EventDataService } from "./event-data.service";

 describe("EventDataService", () => {
  let mockBackend: MockBackend;
  let service: EventDataService;
  let injector: Injector;

  beforeEach(() => {
   TestBed.configureTestingModule({
     imports: [HttpModule],
      providers: [
      { provide: XHRBackend, useClass: MockBackend },
      EventDataService
      ]
    });

    injector = getTestBed();
  });

  beforeEach(() => {
    mockBackend = injector.get(XHRBackend);
    service = injector.get(EventDataService);
  });

您应该需要创建一个测试模块并模拟响应,以测试测试模块中服务方法提供的依赖关系

 import { async, getTestBed, TestBed, inject } from "@angular/core/testing";
 import { Response, ResponseOptions, HttpModule, XHRBackend } from "@angular/http";
 import { MockBackend, MockConnection } from "@angular/http/testing";
 import { EventDataService } from "./event-data.service";

 describe("EventDataService", () => {
  let mockBackend: MockBackend;
  let service: EventDataService;
  let injector: Injector;

  beforeEach(() => {
   TestBed.configureTestingModule({
     imports: [HttpModule],
      providers: [
      { provide: XHRBackend, useClass: MockBackend },
      EventDataService
      ]
    });

    injector = getTestBed();
  });

  beforeEach(() => {
    mockBackend = injector.get(XHRBackend);
    service = injector.get(EventDataService);
  });

尽管我完全同意这样一种普遍看法,即您应该在这个场景中使用TestBed来消除您的Http依赖(毕竟,这是Angular为什么首先有依赖注入的一个巨大动力),但我发现您的方法中存在一些错误,这似乎表明了一些误解

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
返回一个可观察的。因此,如果您想出于测试目的伪造Http,您的伪造Http实例需要有一个
get
方法,并且该get方法需要返回一个可观察的

综合以上所有内容,如果我想在不使用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在您有多个依赖项时简化了事情,而且服务似乎总是以这种方式增长)。但归根结底,构造函数依赖项注入,就像Angular使用的一样,很容易理解-传递依赖项(假或真)作为您试图创建其实例的类的构造函数的参数。

尽管我完全同意这样一种普遍看法,即您应该在这个场景中使用TestBed来消除您的Http依赖关系(毕竟,这是Angular将依赖关系注入放在首位的巨大动力),我在你的方法中发现了一些错误,这似乎表明了一些误解

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
返回一个可观察的。因此,如果您想出于测试目的伪造Http,您的伪造Http实例需要有一个
get
方法,并且该get方法需要返回一个可观察的

综合以上所有内容,如果我想在不使用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实例(不是因为t