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。コンポーネント内で動作しているため、動作することがわかっていますが、ここでの目標は、ジャスミンを使用してこれを完全にテストすることです。このようにして、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 を呼び出すことができます。失敗した

「テスト値」は undefined であると予想されます。

編集:それを行う1つの方法は使用することですが、どこにでもsetTimeout追加するのを避けるために、よりきれいなソリューションが必要です。setTimeout(myAssertion, 100)

4

1 に答える 1

1

Use fakeAsync from @angular/core/testing. It allows you to call tick(), which will wait for all currently queued asynchronous tasks to complete before continuing. This gives the illusion of the actions being synchronous. Right after the call to tick() we can write our expectations.

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

So when should we use fakeAsync and when should we use async? This is the rule of thumb that I go by (most of the time). When we are making asynchronous calls inside the test, this is when we should use async. async allows to test to continue until all asynchronous calls are complete. For example

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

In a non async test, the expectation would never occur, as the test would complete before the asynchronous resolution of the promise. With the call to async, the test gets wrapped in a zone, which keeps track of all asynchronous tasks, and waits for them to complete.

Use fakeAsync when the asynchronous behavior is outside the control of the test (like in your case is going on in the pipe). Here we can force/wait for it to complete with the call to tick(). tick can also be passed a millisecond delay to allow more time to pass if needed.

Another option is to mock the service and make it synchronous, as mentioned in this post. When unit testing, if your components in test are dependent on heavy logic in the service, then the component in test is at the mercy of that service working correctly, which kinda defeats the purpose of a "unit" test. Mocking makes sense in a lot of cases.

于 2016-09-15T15:31:24.333 に答える