最初のスイート

スイート: テストのdescribe

describe関数は関連する仕様をグループ化するためのもので、通常はテストファイルごとに最上位に1つ記述します。文字列パラメータは仕様のコレクションに名前を付けるもので、仕様と結合して仕様の完全な名前を作成します。これは、大規模なスイートで仕様を見つけるのに役立ちます。適切に名前付けると、仕様は従来のBDDスタイルの完全な文章として読み取れます。

仕様

仕様は、グローバルなJasmine関数itを呼び出すことで定義されます。describeと同様に、文字列と関数を取ります。文字列は仕様のタイトルで、関数が仕様またはテストです。仕様には、コードの状態をテストする1つ以上の期待値が含まれます。Jasmineでの期待値は、真または偽のいずれかのアサーションです。すべての期待値が真の仕様は合格仕様です。1つ以上の期待値が偽の仕様は失敗仕様です。

describe("A suite", function() {
    it("contains spec with an expectation", function() {
        expect(true).toBe(true);
    });
});

単なる関数です

describeitブロックは関数なので、テストを実装するために必要な実行可能なコードを含めることができます。JavaScriptのスコープルールが適用されるため、describeで宣言された変数は、スイート内の任意のitブロックで使用できます。

describe("A suite is just a function", function() {
    let a;

    it("and so is a spec", function() {
        a = true;

        expect(a).toBe(true);
    });
});

期待値

期待値は、実際の値(実際の値と呼ばれる)を取る関数expectを使用して構築されます。期待される値を取るマッチャ関数とチェーンで連結されます。

describe("The 'toBe' matcher compares with ===", function() {

マッチャ

各マッチャは、実際の値と期待される値の間のブール値の比較を実装します。期待値が真か偽かをJasmineに報告する責任があります。次に、Jasmineは仕様に合格するか不合格にします。

    it("and has a positive case", function() {
        expect(true).toBe(true);
    });

マッチャにnotをチェーンしてマッチャを呼び出す前にexpectを呼び出すことで、任意のマッチャを否定アサーションとして評価できます。

    it("and can have a negative case", function() {
        expect(false).not.toBe(true);
    });

Jasmineには豊富なマッチャセットが組み込まれています。完全なリストはAPIドキュメントにあります。プロジェクトのドメインで、Jasmineに含まれていない特定のアサーションが要求される場合は、カスタムマッチャを作成することもできます。

});

設定と終了

複製された設定と終了コードをテストスイートでDRYアップするために、JasmineはグローバルなbeforeEachafterEachbeforeAll、およびafterAll関数を提供します。

describe("A suite with some shared setup", function() {
    let foo = 0;

名前が示すように、beforeEach関数は、呼び出されたdescribe内の各仕様の前に1回呼び出されます

    beforeEach(function() {
        foo += 1;
    });

およびafterEach関数は、各仕様の後に1回呼び出されます。

    afterEach(function() {
        foo = 0;
    });

beforeAll関数は、describe内のすべての仕様が実行される前に1回のみ呼び出されます

    beforeAll(function() {
        foo = 1;
    });

およびafterAll関数は、すべての仕様が完了した後に呼び出されます

    afterAll(function() {
        foo = 0;
    });
});

beforeAllafterAllは、設定と終了に費用のかかるテストスイートを高速化するために使用できます。

ただし、beforeAllafterAllを使用する際は注意してください!それらは仕様間でリセットされないため、仕様間に誤って状態が漏洩し、誤って合格または失敗する可能性があります。

thisキーワード

変数をbeforeEachitafterEach間で共有する別の方法は、thisキーワードを使用することです。各仕様のbeforeEach/it/afterEachthisは同じ空のオブジェクトとして、次の仕様のbeforeEach/it/afterEach用に空に戻されます。

注: thisキーワードを使用して変数を共有する場合は、functionキーワードを使用し、矢印関数を使用しないでください。

describe("A spec", function() {
    beforeEach(function() {
        this.foo = 0;
    });

    it("can use the `this` to share state", function() {
        expect(this.foo).toEqual(0);
        this.bar = "test pollution?";
    });

    it("prevents test pollution by having an empty `this` " +
            "created for the next spec", function() {
        expect(this.foo).toEqual(0);
        expect(this.bar).toBe(undefined);
    });
});

failを使用して仕様を手動で失敗させる

fail関数は仕様を失敗させます。パラメータとしてエラーメッセージまたはErrorオブジェクトを取ることができます。

describe("A spec using the fail function", function() {
    function foo(x, callBack) {
        if (x) {
            callBack();
        }
    };

    it("should not call the callBack", function() {
        foo(false, function() {
            fail("Callback has been called");
        });
    });
});

describeブロックをネストする

describeの呼び出しをネストして、任意のレベルで仕様を定義できます。これにより、関数からなるツリーとしてスイートを作成できます。仕様を実行する前に、Jasmineはツリーの各beforeEach関数を順番に実行します。仕様が実行された後、Jasmineは同様にafterEach関数を順番に実行します。

describe("A spec", function() {
    let foo;

    beforeEach(function() {
        foo = 0;
        foo += 1;
    });

    afterEach(function() {
        foo = 0;
    });

    it("is just a function, so it can contain any code", function() {
        expect(foo).toEqual(1);
    });
    
    it("can have more than one expectation", function() {
        expect(foo).toEqual(1);
        expect(true).toEqual(true);
    });

    describe("nested inside a second describe", function() {
        let bar;

        beforeEach(function() {
          bar = 1;
        });
    
        it("can reference both scopes as needed", function() {
          expect(foo).toEqual(bar);
        });
    });
});

スイートを無効にする

xdescribe関数を使用してスイートを無効にすることができます。これらのスイートと内部の仕様は実行時にスキップされ、結果は保留として表示されます。

fdescribe関数を使用してスイートに焦点を当てることもできます。つまり、fdescribeスイートのみが実行されます。

xdescribe("A spec", function() {
    let foo;
    
    beforeEach(function() {
        foo = 0;
        foo += 1;
    });
    
    it("is just a function, so it can contain any code", function() {
        expect(foo).toEqual(1);
    });
});

保留中の仕様

保留中の仕様は実行されませんが、名前は保留中として結果に表示されます。

describe("Pending specs", function() {

xitを使用して宣言された仕様は保留中としてマークされます。

    xit("can be declared 'xit'", function() {
        expect(true).toBe(false);
    });

関数本体なしで宣言された仕様も、結果では保留中としてマークされます。

    it("can be declared with 'it' but without a function");

仕様本体のどこからでもpending関数を呼び出すと、予期される結果に関係なく、仕様は保留中としてマークされます。pendingに渡された文字列は理由として扱われ、スイートが完了したときに表示されます。

fit関数を使用してテストに焦点を当てることもできます。つまり、fitテストのみが実行されます。

    it("can be declared by calling 'pending' in the spec body", function() {
        expect(true).toBe(false);
        pending('this is why it is pending');
    });
});

スパイ

Jasmineにはスパイと呼ばれるテストダブル関数があります。スパイは任意の関数をスタブし、それへの呼び出しとすべての引数を追跡します。スパイはそれが定義されているdescribeまたはitブロックにのみ存在し、各仕様後に削除されます。スパイと対話するための特別なマッチャーがあります。

describe("A spy", function() {
    let foo;
    let bar = null;

    beforeEach(function() {
        foo = {
            setBar: function(value) {
                bar = value;
            }
        };

andを使用して、スパイが呼び出されたときに何を行うかを定義できます。

        spyOn(foo, 'setBar');

        foo.setBar(123);
        foo.setBar(456, 'another param');
    });

toHaveBeenCalledマッチャーは、スパイが呼び出された場合にパスします。

    it("tracks that the spy was called", function() {
        expect(foo.setBar).toHaveBeenCalled();
    });

toHaveBeenCalledTimesマッチャーは、スパイが指定された回数呼び出された場合にパスします。

    it("tracks that the spy was called x times", function() {
        expect(foo.setBar).toHaveBeenCalledTimes(2);
    });

toHaveBeenCalledWithマッチャーは、引数リストがスパイへの記録された呼び出しのいずれかと一致する場合にtrueを返します。

    it("tracks all the arguments of its calls", function() {
        expect(foo.setBar).toHaveBeenCalledWith(123);
        expect(foo.setBar).toHaveBeenCalledWith(456, 'another param');
    });

    it("stops all execution on a function", function() {
        expect(bar).toBeNull();
    });

callsを使用して、スパイがその呼び出しについて追跡するすべてのデータを取得します

    it("tracks if it was called at all", function() {
        foo.setBar();

        expect(foo.setBar.calls.any()).toEqual(true);
    });
});

スパイ: createSpy

スパイする関数が存在しない場合、jasmine.createSpyは「ベア」スパイを作成できます。このスパイは他のスパイと同じように機能します(コールの追跡、引数など)。しかし、その背後には実装がありません。

describe("A spy, when created manually", function() {
    let whatAmI;

    beforeEach(function() {
        whatAmI = jasmine.createSpy('whatAmI');

        whatAmI("I", "am", "a", "spy");
    });

    it("tracks that the spy was called", function() {
        expect(whatAmI).toHaveBeenCalled();
    });
});

スパイ: createSpyObj

複数のスパイを含むモックを作成するには、jasmine.createSpyObjを使用して文字列の配列を渡します。各文字列がスパイであるプロパティを持つオブジェクトが返されます。

describe("Multiple spies, when created manually", function() {
    let tape;

    beforeEach(function() {
        tape = jasmine.createSpyObj(
            'tape',
            ['play', 'pause', 'stop', 'rewind']
        );

        tape.play();
        tape.pause();
        tape.rewind(0);
    });

    it("creates spies for each requested function", function() {
        expect(tape.play).toBeDefined();
        expect(tape.pause).toBeDefined();
        expect(tape.stop).toBeDefined();
        expect(tape.rewind).toBeDefined();
    });
});

詳細なマッチング

正確な等価性で一致させたくない場合があります。Jasmineは、数のアシンメトリカル等価性テスターを提供しています。

describe("Matching with finesse", function() {

jasmine.anyは、期待値としてコンストラクターまたは「クラス」名を受け取ります。コンストラクターが実際の値のコンストラクターと一致する場合にtrueを返します。

    describe("jasmine.any", function() {
        it("matches any value", function() {
            expect({}).toEqual(jasmine.any(Object));
            expect(12).toEqual(jasmine.any(Number));
        });
    
        describe("when used with a spy", function() {
            it("is useful for comparing arguments", function() {
                const foo = jasmine.createSpy('foo');
                foo(12, function() {
                    return true;
                });
    
                expect(foo).toHaveBeenCalledWith(
                    jasmine.any(Number), jasmine.any(Function)
                );
            });
        });
    });

jasmine.anythingは、実際の値がnullまたはundefinedでない場合にtrueを返します。

    describe("jasmine.anything", function() {
        it("matches anything", function() {
            expect(1).toEqual(jasmine.anything());
        });

        describe("when used with a spy", function() {
            it("is useful when the argument can be ignored", function() {
                const foo = jasmine.createSpy('foo');
                foo(12, function() {
                    return false;
                });

                expect(foo).toHaveBeenCalledWith(12, jasmine.anything());
            });
        });
    });

jasmine.objectContainingは、期待値が実際のオブジェクトにおける特定のキー/バリューペアのみを気にする場合に使用します。

    describe("jasmine.objectContaining", function() {
        let foo;

        beforeEach(function() {
            foo = {
                a: 1,
                b: 2,
                bar: "baz"
            };
        });

        it("matches objects with the expect key/value pairs", function() {
            expect(foo).toEqual(jasmine.objectContaining({
                bar: "baz"
            }));
            expect(foo).not.toEqual(jasmine.objectContaining({
                c: 37
            }));
        });

        describe("when used with a spy", function() {
            it("is useful for comparing arguments", function() {
                const callback = jasmine.createSpy('callback');

                callback({
                    bar: "baz"
                });

                expect(callback).toHaveBeenCalledWith(
                    jasmine.objectContaining({ bar: "baz" })
                );
            });
        });
    });

jasmine.arrayContainingは、期待値が配列内の一部の値のみ気にする場合に使用します。

    describe("jasmine.arrayContaining", function() {
        let foo;

        beforeEach(function() {
            foo = [1, 2, 3, 4];
        });

        it("matches arrays with some of the values", function() {
            expect(foo).toEqual(jasmine.arrayContaining([3, 1]));
            expect(foo).not.toEqual(jasmine.arrayContaining([6]));
        });

        describe("when used with a spy", function() {
            it("is useful when comparing arguments", function() {
                const callback = jasmine.createSpy('callback');

                callback([1, 2, 3, 4]);

                expect(callback).toHaveBeenCalledWith(
                    jasmine.arrayContaining([4, 2, 3])
                );
                expect(callback).not.toHaveBeenCalledWith(
                    jasmine.arrayContaining([5, 2])
                );
            });
        });
    });

jasmine.stringMatchingは、大規模なオブジェクト内の文字列とまったく一致させたくないとき、またはスパイの期待値内で文字列の一部と一致させたいときに使用します。

    describe('jasmine.stringMatching', function() {
        it("matches as a regexp", function() {
            expect({foo: 'bar'}).toEqual({
                foo: jasmine.stringMatching(/^bar$/)
            });
            expect({foo: 'foobarbaz'}).toEqual({
                foo: jasmine.stringMatching('bar')
            });
        });

        describe("when used with a spy", function() {
            it("is useful for comparing arguments", function() {
                const callback = jasmine.createSpy('callback');

                callback('foobarbaz');

                expect(callback).toHaveBeenCalledWith(
                    jasmine.stringMatching('bar')
                );
                expect(callback).not.toHaveBeenCalledWith(
                    jasmine.stringMatching(/^bar$/)
                );
            });
        });
    });

非相称等価性カスタムテスター

完全に等しい必要なく、何かが特定の基準を満たすことを確認したい場合、asymmetricMatch関数を持つオブジェクトを単純に提供することで、非相称等価性カスタムテスターを指定することもできます。

    describe("custom asymmetry", function() {
        const tester = {
            asymmetricMatch: function(actual) {
                const secondValue = actual.split(',')[1];
                return secondValue === 'bar';
            }
        };

        it("dives in deep", function() {
            expect("foo,bar,baz,quux").toEqual(tester);
        });

        describe("when used with a spy", function() {
            it("is useful for comparing arguments", function() {
                const callback = jasmine.createSpy('callback');

                callback('foo,bar,baz');

                expect(callback).toHaveBeenCalledWith(tester);
            });
        });
    });
});

Jasmine Clock

Jasmine Clockは、時間に依存するコードをテストするために利用できます。

describe("Manually ticking the Jasmine Clock", function() {
    let timerCallback;

時間を操作する必要のあるスペックまたはスイート内でjasmine.clock().installを呼び出すことでインストールされます。

    beforeEach(function() {
        timerCallback = jasmine.createSpy("timerCallback");
        jasmine.clock().install();
    });

完了したら、必ずクロックをアンインストールして元々の関数を復元してください。

    afterEach(function() {
        jasmine.clock().uninstall();
    });

JavaScript Timeout 関数のモッキング

setTimeoutまたはsetIntervalは、クロックが時間を進めた場合のみ、登録関数を1回実行することで同期的に実行できます。

登録関数を呼び出すには、ミリ秒数を入力するjasmine.clock().tick関数を使用して、時間を進めます。

    it("causes a timeout to be called synchronously", function() {
        setTimeout(function() {
            timerCallback();
        }, 100);

        expect(timerCallback).not.toHaveBeenCalled();

        jasmine.clock().tick(101);

        expect(timerCallback).toHaveBeenCalled();
    });

    it("causes an interval to be called synchronously", function() {
        setInterval(function() {
            timerCallback();
        }, 100);

        expect(timerCallback).not.toHaveBeenCalled();

        jasmine.clock().tick(101);
        expect(timerCallback.calls.count()).toEqual(1);

        jasmine.clock().tick(50);
        expect(timerCallback.calls.count()).toEqual(1);

        jasmine.clock().tick(50);
        expect(timerCallback.calls.count()).toEqual(2);
    });

日付のモッキング

Jasmine Clockを使用すると、現在の日付をモックすることもできます。

    describe("Mocking the Date object", function(){
        it("mocks the Date object and sets it to a given time", function() {
            const baseTime = new Date(2013, 9, 23);

mockDateに基本時間を入力しない場合、現在の日付が使用されます。

            jasmine.clock().mockDate(baseTime);

            jasmine.clock().tick(50);
            expect(new Date().getTime()).toEqual(baseTime.getTime() + 50);
        });
    });
});

非同期サポート

Jasmineは、非同期操作のテストが必要なスペックの実行をサポートしています。beforeAllafterAllbeforeEachafterEach、およびitに入力する関数は、asyncとして宣言できます。

Jasmineはまた、明示的にPromiseを返したり、コールバックを入力したりする非同期関数をサポートします。詳細については、非同期作業チュートリアルを参照してください。

describe("Using async/await", function() {
    beforeEach(async function() {
        await soon();
        value = 0;
    });

上部のbeforeEach呼び出しから返されたPromiseが解決されるまで、このスペックは開始されません。また、このスペックはその返されるPromiseが解決されるまで完了しません。

    it("supports async execution of test preparation and expectations",
        async function() {
            await soon();
            value++;
            expect(value).toBeGreaterThan(0);
        }
    );
});

デフォルトでは、jasmineは非同期スペックが完了するのを5秒間待機してから、タイムアウトエラーが発生します。doneが呼び出される前にタイムアウトが満了した場合、現在のスペックは失敗済みとしてマークされ、スイートの実行はdoneが呼び出されたかのように続行されます。

特定のスペックがより早く失敗したり、より多くの時間を必要とする場合は、itなどにタイムアウト値を渡すことで調整できます。

スイート全体が異なるタイムアウトを持つ必要がある場合、jasmine.DEFAULT_TIMEOUT_INTERVALは、どのdescribeの外側でもグローバルに設定できます。

describe("long asynchronous specs", function() {
    beforeEach(async function() {
        await somethingSlow();
    }, 1000);

    it("takes a long time", function() {
        await somethingReallySlow();
    }, 10000);

    afterEach(function() {
        await somethingSlow();
        }, 1000);
    });

    function soon() {
        return new Promise(function(resolve, reject) {
            setTimeout(function() {
                resolve();
            }, 1);
        });
    }
});