1

语境

我有一个基本的PipeTransform,期望它是异步的。为什么?因为我有自己的 i18n 服务(由于解析、复数和其他限制,我自己做了),它返回一个Promise<string>

@Pipe({
    name: "i18n",
    pure: false
})
export class I18nPipe implements PipeTransform {

    private done = false;

    constructor(private i18n:I18n) {
    }

    value:string;

    transform(value:string, args:I18nPipeArgs):string {
        if(this.done){
            return this.value;
        }
        if (args.plural) {
            this.i18n.getPlural(args.key, args.plural, value, args.variables, args.domain).then((res) => {
                this.value = res;
                this.done = true;
            });
        }
        this.i18n.get(args.key, value, args.variables, args.domain).then((res) => {
            this.done = true;
            this.value = res;
        });
        return this.value;
    }
}

该管道运行良好,因为唯一延迟的调用是第一个调用(I18nService使用延迟加载,只有在找不到密钥时才加载 JSON 数据,所以基本上,第一个调用将被延迟,其他调用是即时的但仍然异步)。

问题

我不知道如何使用 来测试这个管道Jasmine,因为它在一个我知道它可以工作的组件中工作,但这里的目标是使用 jasmine 对其进行全面测试,这样我就可以将它添加到 CI 例程中。

上述测试:

describe("Pipe test", () => {

        it("can call I18n.get.", async(inject([I18n], (i18n:I18n) => {
            let pipe = new I18nPipe(i18n);
            expect(pipe.transform("nope", {key: 'test', domain: 'test domain'})).toBe("test value");
        })));
});

失败是因为由于给出的结果I18nService是异步的,所以返回的值在同步逻辑中是未定义的。

I18n 管道测试可以调用 I18n.get。失败的

预期未定义为“测试值”。

编辑:一种方法是使用setTimeout,但我想要一个更漂亮的解决方案,以避免setTimeout(myAssertion, 100)到处添加。

4

1 回答 1

1

使用fakeAsync@angular/core/testing. 它允许您调用tick(),这将等待所有当前排队的异步任务完成,然后再继续。这给人一种动作是同步的错觉。在调用之后,tick()我们可以写下我们的期望。

import { fakeAsync, tick } from '@angular/core/testing';

it("can call I18n.get.", fakeAsync(inject([I18n], (i18n:I18n) => {
  let pipe = new I18nPipe(i18n);
  let result = pipe.transform("nope", {key: 'test', domain: 'test domain'});
  tick();
  expect(result).toBe("test value");
})));

那么我们应该什么时候使用fakeAsync,什么时候应该使用async呢?这是我(大部分时间)遵循的经验法则。当我们在测试中进行异步调用,我们应该使用async. async允许测试继续,直到所有异步调用完成。例如

it('..', async(() => {
  let service = new Servce();
  service.doSomething().then(result => {
    expect(result).toBe('hello');
  });
});

在非async测试中,期望永远不会发生,因为测试将在承诺的异步解决之前完成。通过调用async,测试被包裹在一个区域中,该区域跟踪所有异步任务,并等待它们完成。

当异步行为超出测试的控制范围时使用fakeAsync(例如在您的情况下正在管道中进行)。在这里,我们可以通过调用来强制/等待它完成tick()tick如果需要,也可以传递毫秒延迟以允许更多时间通过。

另一种选择是模拟服务并使其同步,如本文所述。在单元测试时,如果您在测试中的组件依赖于服务中的繁重逻辑,那么测试中的组件将受该服务正常工作的支配,这有点违背“单元”测试的目的。在很多情况下,嘲笑是有意义的。

于 2016-09-15T15:31:24.333 回答