【问题标题】:Test an async PipeTransform测试异步 PipeTransform
【发布时间】:2017-01-23 16:34:31
【问题描述】:

上下文

我有一个基本的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)

【问题讨论】:

    标签: asynchronous angular jasmine angular2-testing angular2-pipe


    【解决方案1】:

    使用来自@angular/core/testingfakeAsync。它允许您调用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?这是我(大部分时间)遵循的经验法则。当我们在测试内部进行异步调用时,我们应该使用asyncasync 允许测试继续,直到所有异步调用完成。例如

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

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

    当异步行为不在测试的控制范围内时使用fakeAsync(就像你的情况是在管道中进行的那样)。在这里,我们可以通过调用tick() 来强制/等待它完成。 tick 也可以传递毫秒延迟,以便在需要时留出更多时间。

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

    【讨论】:

    • 另一个涉及的问题是管道的转换只被调用一次,所以返回基本上总是未定义的。我通过在我的测试文件中创建一个递归方法来解决这个问题,只要我有未定义的结果,我就可以调用管道转换,并使用最大调用堆栈。
    猜你喜欢
    • 2019-11-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-06-12
    • 1970-01-01
    • 1970-01-01
    • 2013-03-16
    相关资源
    最近更新 更多