スイート: テストの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);
});
});
|
単なる関数です
describe とit ブロックは関数なので、テストを実装するために必要な実行可能なコードを含めることができます。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はグローバルなbeforeEach、afterEach、beforeAll、および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;
});
});
|
beforeAll とafterAll は、設定と終了に費用のかかるテストスイートを高速化するために使用できます。
ただし、beforeAll とafterAll を使用する際は注意してください!それらは仕様間でリセットされないため、仕様間に誤って状態が漏洩し、誤って合格または失敗する可能性があります。
|
|
this キーワード
変数をbeforeEach 、it 、afterEach 間で共有する別の方法は、this キーワードを使用することです。各仕様のbeforeEach /it /afterEach のthis は同じ空のオブジェクトとして、次の仕様の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は、非同期操作のテストが必要なスペックの実行をサポートしています。beforeAll 、afterAll 、beforeEach 、afterEach 、および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);
});
}
});
|