测试异步 PipeTransform
Test an async PipeTransform
上下文
我有一个基本的 PipeTransform
,期望它是异步的。为什么?因为我有自己的 i18n 服务(由于解析、多元化和其他限制,我自己做了)并且它 returns a 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 Pipe test can call I18n.get. FAILED
Expected undefined to be 'test value'.
编辑:一种方法是使用 setTimeout
但我想要一个更漂亮的解决方案,以避免在任何地方添加 setTimeout(myAssertion, 100)
。
使用 @angular/core/testing
中的 fakeAsync
。它允许您调用 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
(就像你的情况在管道中发生一样)。在这里我们可以 force/wait 来完成对 tick()
的调用。 tick
也可以传递一个毫秒延迟,以便在需要时允许更多时间传递。
另一种选择是模拟服务并使其同步,如 中所述。单元测试时,如果测试中的组件依赖于服务中的大量逻辑,那么测试中的组件将受制于该服务是否正常工作,这有点违背 "unit" 测试的目的。在很多情况下模拟是有意义的。
上下文
我有一个基本的 PipeTransform
,期望它是异步的。为什么?因为我有自己的 i18n 服务(由于解析、多元化和其他限制,我自己做了)并且它 returns a 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 Pipe test can call I18n.get. FAILED
Expected undefined to be 'test value'.
编辑:一种方法是使用 setTimeout
但我想要一个更漂亮的解决方案,以避免在任何地方添加 setTimeout(myAssertion, 100)
。
使用 @angular/core/testing
中的 fakeAsync
。它允许您调用 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
(就像你的情况在管道中发生一样)。在这里我们可以 force/wait 来完成对 tick()
的调用。 tick
也可以传递一个毫秒延迟,以便在需要时允许更多时间传递。
另一种选择是模拟服务并使其同步,如