如何测试响应承诺的组件行为

How to test component behaviour in response to promise

首先,我不认为它是重复的,因为 SO 问题和答案描述了 angular1 中承诺处理的测试,并通过调用 $timeout.flush()$rootScope.$apply() 解决,因此不适用于此Angular2例.

我的问题是如何测试 Angular2 组件的逻辑,该组件在解析承诺 后正在执行。 (最好是茉莉花)。

为了说明它,我修改了 Angular2 文档中的快速入门示例。简单组件列出现有英雄并允许注册新英雄。英雄列表和注册由服务完成,returns 以 Promises 响应(带有英雄列表或新注册的英雄对象)。

现在,一旦注册了新英雄(并且 Promise 已解决),我希望组件能够选择英雄并再次检索英雄列表:

...
register(heroName:string) {
  this.postRegistration(this._heroService.registerHero(heroName));
}
//this extra method helps testing without stubing of the service
postRegistration(heroPromise:Promise<Hero>) {
  heroPromise.then( hero => {
      //I want to test that the two actions below took place
      this.selectedHero = hero;
      this.getHeroes(); })
}

getHeroes() {
this._heroService.getHeroes().then(heroes => this.heroes = heroes);
}

我将注册分为两种方法,因此可以在不对服务进行存根的情况下进行测试

将 Jasmine 异步测试与 done() 结合使用,我可以轻松地测试服务行为,即。它 returns 正确的承诺。 但是如何测试组件调用 getHeros 来刷新其列表或正确设置所选英雄。

我设法使用规范测试设置变量:

it("postRegistration sets selectedHero as registered one",done =>{
    let promise = service.registerHero("Bosek"); //Promise.resolve(hero);
    component.postRegistration(promise);
    promise.then( hero => {
        expect(component.selectedHero).toBe(hero);
        expect(component.heroes.indexOf(hero)).toBeGreaterThan(0);
        done();
    })
}) 

我 "hooked" 我对传递给组件的相同承诺的断言。

我的第一个问题:我是幸运还是它保证如果我订阅相同的承诺并从它的 'then' 子句调用 jasmine done() 它会等到我的其他 "subscribers"(在那种情况下是组件)处理承诺和后续承诺。

其次,有没有办法在测试期间强制所有Promise同步,这样就可以解决整个问题。

我找到了允许 "firing" 承诺的模拟承诺项目 https://github.com/charleshansen/mock-promises,但它已经死了 2 年了。

这个问题应该很普遍,应该有一个标准答案。

我在 Angular2Jasmine 的上下文中提问,但是,我对简单的 'unit test' 方法也是如此,例如当前测试完全独立于 Angular 框架。请打字稿,因为我通常不使用 JS。

完整代码(它是 Angular2 Hero 快速入门示例的简单克隆)位于:https://github.com/tzielins/angular-start-project

My first question:

您应该始终确保return 承诺

register(heroName:string) {
  return this.postRegistration(this._heroService.registerHero(heroName));
}
//this extra method helps testing without stubing of the service
postRegistration(heroPromise:Promise<Hero>) {
  return heroPromise.then( hero => {
      //I want to test that the two actions below took place
      this.selectedHero = hero;
      return this.getHeroes(); })
}

getHeroes() {
  return this._heroService.getHeroes().then(heroes => this.heroes = heroes);
}

这样,当您在 return 值上调用 .then(...) 时,异步部分将被链接起来,否则异步调用将成为它们自己断开的事件链,并且您无法获得有关它们何时调用的任何信息执行或完成时。

Secondly

有一个 fakeAsync 应该允许用于测试 Angular2 testing with fakeAsync (我自己没试过)

否则 promises 是异步的,没有什么可以改变它。

@Gunter 的回答让我走上了正轨,但由于他没有明确解决问题,我正在写自己的答案。

My first question

我确实只是幸运。对同一个承诺进行后续调用,对之前的调用没有任何影响。虽然这个测试通过了,但其他类似模式的测试失败了。

从 "bussiness" 方法返回承诺并链接到它并不总是一个选项,因为它取决于方法逻辑,因此 Gunter 的建议并不总是可以遵循。

Secondly

Gunter 提到了 Angular2 fakeAsync,这是测试 Promise 处理的副作用的正确方法。虽然它是 angular2 测试库的一部分,但它可以以 'a unit test' 的方式使用,因为它不需要注入或其他 angular 依赖项。

使用它,在 fakeAsync 体内调用 flushMicrotasks 并测试副作用:

import {it,fakeAsync,tick,flushMicrotasks} from 'angular2/testing';
...
    it('correct side effect after processing promise',<any>fakeAsync(()  => {

        let p = Promise.resolve(X);

        //invoking business logic
        component.dealWithIt(p);

        flushMicrotasks();

        //now the side effects can be tested in synchronous way
        expect(component.selectedHero).toBe(hero);
        ...
    }));

tick() 对承诺有类似的影响。

即使是在业务方法中创建的新承诺也可以保证完成。我在这里问了这个问题: 我还发现 Angular 测试规范证实了这一点。