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

时间:2017-03-27 21:45:45

标签: unit-testing angular karma-runner

我想测试一个简单的Angular 2数据服务。该服务使用Http,但没有别的。在quickstart guide中它说:

  

然而,探索内在逻辑往往更有成效   具有独立单元测试的应用程序类,不依赖于它们   角。这些测试通常更小,更易于阅读,编写和   维护。

编写一个隔离单元测试的例子是,对于简单的服务,你可以通过在每个测试中创建一个新实例来测试服务......可能是这样的:

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 { 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!

2 个答案:

答案 0 :(得分:5)

虽然我全心全意地同意你应该在这个场景中使用TestBed消除你的Http依赖关系的一般情绪(毕竟,这对于Angular为什么一开始依赖注入是一个巨大的动力),我&# 39;我看到你的方法中有些错误似乎表明存在一些误解。

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

所以,你应该这样做:

plugin.tx_powermail {
     settings.setup {
             validation {
                     customValidation {
                             100 = In2code\Powermailextended\Domain\Validator\your_php_file_name
                     }
             }
     }
     _LOCAL_LANG.default.validationerror_validation.100 = Please add a ZIP with 8 begginning
}

其中x是Http类型的变量。可能不太明显的是,Typescript不是像Java这样的语言 - 如果你想要的话,Typescript可以让你自己走开。所以,你可以,例如,只需创建一个新对象,然后说出它的类型&#39; Http&#39;而且Typescript编译器会假设你知道自己在做什么,然后让你继续。

所以你可以这样做:

let dataService = new EventDataService(x);

第一行是告诉编译器我希望{}的类型是Http,而Typescript会让你不知所措并假设你知道自己在做什么。

所以,你可以使用这种技术来获得一个实例&#39; Http用于您的测试 - 我使用&#39;实例&#39;非常松散。

但是,如果查看EventDataService,它会期望传递给其构造函数的http对象具有let x = ({ } as Http); let dataService = new EventDataService(x); 方法,该方法返回一个可以调用map的对象。换句话说,它期望get返回一个Observable。所以,如果你想伪造Http用于测试目的,你的假Http实例需要有get方法,并且get方法需要返回一个Observable。

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

get

我会这样接近吗?可能不是 - 我只是使用TestBed来获取一个虚假的Http实例(不是因为这种方法很难,而是因为当你有多个依赖项时,TestBed简化了事情,而且服务似乎总是以这种方式增长)。但是在一天结束时,构造函数依赖注入(就像Angular使用的那样)很容易理解 - 将依赖项(伪造或真实)作为参数传递给您尝试创建实例的类的构造函数。

答案 1 :(得分:1)

您应该创建一个测试模块并模拟响应以测试服务方法在测试模块中提供依赖性

 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);
  });