Angularでは、Jasmine、Angular testing utilities、Karma、Protractorなどが推奨されるテストツールとなっています。Jasmineはテストフレームワークと言われるもので、アプリケーションのテストに必要な機能を備えています。Jasmineは、テストの実行環境から、アサーション、モックやスパイなど豊富な機能を備えているのが特徴でもあります。個人的にAngularを使うことになって、同時にテストを書く際にこのJasmineを使うことになりました。今回の記事では、テストを書く際によく使われるアサーションのMatcher(テストの評価条件を定義するメソッド)について使い方をまとめてみました。よかったら参考にしてみてください。
目次
- expect(A).toBe(B)
- expect(A).toEqual(B)
- expect(A).toBeCloseTo(B, C)
- expect(A).toBeGreaterThan(B)
- expect(A).toBeGreaterThanOrEqual(B)
- expect(A).toBeLessThan(B)
- expect(A).toBeLessThanOrEqual(B)
- expect(A).toBeTruthy()
- expect(A).toBeFalsy()
- expect(A).toBeNaN()
- expect(A).toBeNull()
- expect(A).toBeDefined()
- expect(A).toBeUndefined()
- expect(A).toContain(B)
- expect(A).toMatch(B)
- expect(A).toHaveBeenCalled()
- expect(A).toHaveBeenCalledBefore(B)
- expect(A).toHaveBeenCalledTimes(B)
- expect(A).toHaveBeenCalledWith(B)
- expect(A).toThrow(B)
- expect(A).toThrowError(B, C)
JasmineのMatchers一覧
Matcherは、テストの評価条件を定義するメソッドです。expect()メソッドにテストする値を定義し、Matcherにはテスト結果(期待する値)を定義し、expect(A).matcherのメソッド(B)の形で、それぞれの値を比較することでテストを行います。
以下はJasmineで用意されているMatcherの全メソッドです。Jasmine v2.6の公式ドキュメントを元にまとめています。書き方はそれほど難しくありません。使用例を見ていただければと思います。
expect(A).toBe(B)
概要
AがBと===(厳密等価)であることをexpectします
パラメータ
toBe(expected)
- @param {Object} 比較するための期待値
使用例
it('aはbと厳密等価である', () => {
let [a, b] = [30, 30];
expect(a).toBe(b); // => SUCCESS
});
it('aはbと厳密等価である', () => {
let [a, b] = [30, '30'];
expect(a).toBe(b); // => FAILED
});
expect(A).toEqual(B)
概要
AがBと同値比較で等しいことをexpectします
パラメータ
toEqual(expected)
- @param {Object} 比較する期待値
使用例
it('aはbと同値比較で等しい', () => {
let [a, b] = [
{"foo": ['bar', 'baz']},
{"foo": ['bar', 'baz']},
];
expect(a).toEqual(b); // => SUCCESS
});
it('aはbと同値比較で等しい', () => {
let [a, b] = [
{"foo": ['bar', 'baz']},
{"foo": ['bar', 'bazz']},
];
expect(a).toEqual(b); // => FAILED
});
expect(A).toBeCloseTo(B, C)
概要
AがBと与えられた少数桁数(C)まで一致していることをexpectします
パラメータ
toBeCloseTo(expected, [precision(opt)])
- @param {Object} 比較するための期待値
- @param {Number} チェックする小数点の数(デフォルト値: 2)
使用例
it('aはbと小数点第2位まで一致する', () => {
let [a, b] = [3.14, 3.1415];
expect(a).toBeCloseTo(b, 2); // => SUCCESS
});
it('aはbと小数点第2位まで一致する', () => {
let [a, b] = [3.14, 3.1];
expect(a).toBeCloseTo(b, 2); // => FAILED
});
expect(A).toBeGreaterThan(B)
概要
AがBより大きいことをexpectします
パラメータ
toBeGreaterThan(expected)
- @param {Number} 比較するための期待値
使用例
it('aはbより大きい', () => {
let [a, b] = [30, 25];
expect(a).toBeGreaterThan(b); // => SUCCESS
});
it('aはbより大きい', () => {
let [a, b] = [30, 30];
expect(a).toBeGreaterThan(b); // => FAILED
});
expect(A).toBeGreaterThanOrEqual(B)
概要
AがBより大きいか、または等しいことをexpectします
パラメータ
toBeGreaterThanOrEqual(expected)
- @param {Number} 比較するための期待値
使用例
it('aはbより大きいか、または等しい', () => {
let [a, b] = [30, 25];
expect(a).toBeGreaterThanOrEqual(b); // => SUCCESS
});
it('aはbより大きいか、または等しい', () => {
let [a, b] = [30, 30];
expect(a).toBeGreaterThanOrEqual(b); // => SUCCESS
});
it('aはbより大きいか、または等しい', () => {
let [a, b] = [30, 35];
expect(a).toBeGreaterThanOrEqual(b); // => FAILED
});
expect(A).toBeLessThan(B)
概要
AがBより小さいことをexpectします
パラメータ
toBeLessThan(expected)
- @param {Number} 比較するための期待値
使用例
it('aはbより小さい', () => {
let [a, b] = [30, 35];
expect(a).toBeLessThan(b); // => SUCCESS
});
it('aはbより小さい', () => {
let [a, b] = [30, 25];
expect(a).toBeLessThan(b); // => FAILED
});
expect(A).toBeLessThanOrEqual(B)
概要
AがBより小さいか、または等しいことをexpectします
パラメータ
toBeLessThanOrEqual(expected)
- @param {Number} 比較するための期待値
使用例
it('aはbより小さいか、または等しい', () => {
let [a, b] = [30, 35];
expect(a).toBeLessThanOrEqual(b); // => SUCCESS
});
it('aはbより小さいか、または等しい', () => {
let [a, b] = [30, 30];
expect(a).toBeLessThanOrEqual(b); // => SUCCESS
});
it('aはbより小さいか、または等しい', () => {
let [a, b] = [30, 25];
expect(a).toBeLessThanOrEqual(b); // => FAILED
});
expect(A).toBeTruthy()
概要
Aがture(真)であることをexpectします
パラメータ
なし
使用例
it('aとbを比較するとtrueと評価される', () => {
let [a, b] = [30, 30]
expect(a === b).toBeTruthy(); // => SUCCESS
});
it('aとbを比較するとtrueと評価される', () => {
let [a, b] = [30, 35]
expect(a === b).toBeTruthy(); // => FAILED
});
expect(A).toBeFalsy()
概要
Aがfalse(偽)であることをexpectします
パラメータ
なし
使用例
it('aとbを比較するとfalseと評価される', () => {
let [a, b] = [30, 35]
expect(a === b).toBeFalsy(); // => SUCCESS
});
it('aとbを比較するとfalseと評価される', () => {
let [a, b] = [30, 30]
expect(a === b).toBeFalsy(); // => FAILED
});
expect(A).toBeNaN()
概要
AがNaNであること(非数)をexpectします
パラメータ
なし
使用例
it('aはNaNである', () => {
let a = NaN;
expect(NaN).toBeNaN(); // => SUCCESS
});
it('aはNaNである', () => {
let a = 30;
expect(a).toBeNaN(); // => FAILED
});
expect(A).toBeNull()
概要
AがNullであることをexpectします
パラメータ
なし
使用例
it('aはnullである', () => {
let a = null;
expect(a).toBeNull(); // => SUCCESS
});
it('aはnullである', () => {
let a = '';
expect(a).toBeNull(); // => FAILED
});
expect(A).toBeDefined()
概要
Aが定義されていることをexpectします
パラメータ
なし
使用例
it('foo.bが定義されている', () => {
let foo = { a: 30, b: 35 };
expect(foo.b).toBeDefined(); // => SUCCESS
});
it('foo.bが定義されている', () => {
let foo = { a: 30 };
expect(foo.b).toBeDefined(); // => FAILED
});
expect(A).toBeUndefined()
概要
Aがundefinedであることをexpectします
パラメータ
なし
使用例
it('foo.bが定義されていない', () => {
let foo = { a: 30 };
expect(foo.b).toBeUndefined(); // => SUCCESS
});
it('foo.bが定義されていない', () => {
let foo = { a: 30, b: 35 };
expect(foo.b).toBeUndefined(); // => FAILED
});
expect(A).toContain(B)
概要
AがBを含んでいることをexpectします
パラメータ
toContain(expected)
- @param {Object} 探すための値
使用例1
it('配列aに1が含まれている', () => {
let a = [1, 2, 3];
expect(a).toContain(1); // => SUCCESS
});
it('配列aに1が含まれている', () => {
let a = ['a', 'b', 'c'];
expect(a).toContain(1); // => FAILED
});
使用例2
it('aの文字列にfが含まれている', () => {
let a = 'foo';
expect(a).toContain('f'); // => SUCCESS
});
it('aの文字列にfが含まれている', () => {
let a = 'bar';
expect(a).toContain('f'); // => FAILED
});
expect(A).toMatch(B)
概要
AがBの正規表現にマッチしていることをexpectします
パラメータ
toMatch(expected)
- @param {RegExp / String} 文字列を探すための値
使用例1
it('aがbの正規表現にマッチする', () => {
let [a, b] = ['my string', /string$/];
expect(a).toMatch(b); // => SUCCESS
});
it('aがbの正規表現にマッチする', () => {
let [a, b] = ['my string', /^string/];
expect(a).toMatch(b); // => FAILED
});
使用例2
it('aがbの文字列にマッチする', () => {
let [a, b] = ['my string', 'ring'];
expect(a).toMatch(b); // => SUCCESS
});
it('aがbの文字列にマッチする', () => {
let [a, b] = ['my string', 'rings'];
expect(a).toMatch(b); // => FAILED
});
expect(A).toHaveBeenCalled()
概要
スパイAが呼び出されていることをexpectします
パラメータ
なし
使用例
describe("toHaveBeenCalled()によるスパイの呼び出しテスト", () => {
// Fooクラスの定義
class Foo {
bar() { // bar()メソッド
return 'bar()';
}
baz() { // baz()メソッド
return 'baz()';
}
}
let foo;
beforeEach(() => {
foo = new Foo(); // Fooクラスのインスタンス化
// fooオブジェクトのbar()メソッドをSpyオブジェクトに追加し、監視対象とする
spyOn(foo, 'bar');
foo.bar(); // foo.bar()メソッドの呼び出し
foo.baz(); // foo.baz()メソッドの呼び出し
});
it('foo.bar()メソッドがスパイとして呼び出されている', () => {
expect(foo.bar).toHaveBeenCalled(); // => SUCCESS
});
it('foo.baz()メソッドがスパイとして呼び出されている', () => {
expect(foo.baz).toHaveBeenCalled(); // => FAILED
});
});
expect(A).toHaveBeenCalledBefore(B)
概要
スパイAがスパイBより先に呼び出されていることをexpectします
パラメータ
toHaveBeenCalledBefore(expected)
- @param {Spy} 実際のスパイより後に呼び出される別のスパイ
使用例
describe("toHaveBeenCalledBefore()によるスパイの呼び出し順のテスト", () => {
// Fooクラスの定義
class Foo {
bar() { // bar()メソッド
return 'bar()';
}
baz() { // baz()メソッド
return 'baz()';
}
}
let foo;
beforeEach(() => {
foo = new Foo(); // Fooクラスのインスタンス化
// fooオブジェクトのbar()メソッドをSpyオブジェクトに追加し、監視対象とする
spyOn(foo, 'bar');
// fooオブジェクトのbaz()メソッドをSpyオブジェクトに追加し、監視対象とする
spyOn(foo, 'baz');
foo.bar(); // foo.bar()メソッドの呼び出し
foo.baz(); // foo.baz()メソッドの呼び出し
});
it('foo.bar()メソッドはfoo.baz()メソッドより先に呼び出されている', () => {
expect(foo.bar).toHaveBeenCalledBefore(foo.baz); // => SUCCESS
});
it('foo.baz()メソッドはfoo.bar()メソッドより先に呼び出されている', () => {
expect(foo.baz).toHaveBeenCalledBefore(foo.bar); // => FAILED
});
});
expect(A).toHaveBeenCalledTimes(B)
概要
スパイAがBで指定された回数呼び出されていることをexpectします
パラメータ
toHaveBeenCalledTimes(expected)
- @param {Number} 期待する呼び出し数
使用例
describe("toHaveBeenCalledTimes()によるスパイの呼び出し回数テスト", () => {
// Fooクラスの定義
class Foo {
bar() { // bar()メソッド
return 'bar()';
}
baz() { // baz()メソッド
return 'baz()';
}
}
let foo;
beforeEach(() => {
foo = new Foo(); // Fooクラスのインスタンス化
// fooオブジェクトのbar()メソッドをSpyオブジェクトに追加し、監視対象とする
spyOn(foo, 'bar');
// foo.bar()メソッドの呼び出し × 2回
foo.bar();
foo.bar();
});
it('foo.bar()メソッドが2回呼び出されている', () => {
expect(foo.bar).toHaveBeenCalledTimes(2); // => SUCCESS
});
it('foo.bar()メソッドが3回呼び出されている', () => {
expect(foo.bar).toHaveBeenCalledTimes(3); // => FAILED
});
});
expect(A).toHaveBeenCalledWith(B)
概要
スパイAがBに指定した引数で少なくとも1回呼び出されていることをexpectします
パラメータ
toHaveBeenCalledWith(expected)
- @param {Object} 期待する引数(繰り返し可能)
使用例
describe("toHaveBeenCalledWith()による引数を渡したスパイの呼び出しテスト", () => {
// Fooクラスの定義
class Foo {
bar(...args) { // bar()メソッド
return args;
}
baz(...args) { // baz()メソッド
return args;
}
}
let foo;
beforeEach(() => {
foo = new Foo(); // Fooクラスのインスタンス化
// fooオブジェクトのbar()メソッドをSpyオブジェクトに追加し、監視対象とする
spyOn(foo, 'bar');
// foo.bar()メソッドの呼び出し
foo.bar('foo', 'bar', 2);
});
it('foo.bar()メソッドが指定された引数で呼び出されている', () => {
expect(foo.bar).toHaveBeenCalledWith('foo', 'bar', 2); // => SUCCESS
});
it('foo.bar()メソッドが指定された引数で呼び出されている', () => {
expect(foo.bar).toHaveBeenCalledWith('foo', 'baz', 3); // => FAILED
});
});
expect(A).toThrow(B)
概要
Aが(Bという)例外を投げることをexpectします
パラメータ
toThrow([expected(opt)])
- @param {Object} 投げられる値。指定しない場合は、何かが投げられたという事実だけをチェックする
使用例1
it('foo関数から例外が投げられる', () => {
let foo = () => {
throw 'bar';
return 'foo';
}
expect(foo).toThrow(); // => SUCCESS
});
it('foo関数から例外が投げられる', () => {
let foo = () => {
return 'foo';
}
expect(foo).toThrow(); // => FAILED
});
使用例2
it('foo関数から「bar」という例外が投げられる', () => {
let foo = () => {
throw 'bar';
return 'foo';
}
expect(foo).toThrow('bar'); // => SUCCESS
});
it('foo関数から「bar」という例外が投げられる', () => {
let foo = () => {
throw 'baz';
return 'foo';
}
expect(foo).toThrow('bar'); // => FAILED
});
expect(A).toThrowError(B, C)
概要
Aが(Bというエラー名で)(Cというメッセージ)のエラーを投げることをexpectします
パラメータ
toThrowError([expected(opt)], [message(opt)])
- @param {Error} ランタイムエラー時に投げられ、インスタンス化されるErrorコンストラクタ。指定しない場合は、Errorが指定される
- @param {RegExp | String} 投げられるエラーにセットされるべきメッセージ
使用例1
it('foo関数からエラーが投げられる', () => {
let foo = () => {
throw new Error('bar');
return 'foo';
}
expect(foo).toThrowError(); // => SUCCESS
});
it('foo関数からエラーが投げられる', () => {
let foo = () => {
return 'foo';
}
expect(foo).toThrowError(); // => FAILED
});
使用例2
it('foo関数からSyntaxErrorというエラーが投げられる', () => {
let foo = () => {
throw new SyntaxError('syntax error');
return 'foo';
}
expect(foo).toThrowError(SyntaxError); // => SUCCESS
});
it('foo関数からSyntaxErrorというエラーが投げられる', () => {
let foo = () => {
throw new TypeError('type error');
return 'foo';
}
expect(foo).toThrowError(SyntaxError); // => FAILED
});
使用例3
it('foo関数からSyntaxErrorという名前で「syntax error」というメッセージのエラーが投げられる', () => {
let foo = () => {
throw new SyntaxError('syntax error');
return 'foo';
}
expect(foo).toThrowError(SyntaxError, 'syntax error'); // => SUCCESS
});
it('foo関数からSyntaxErrorという名前で「syntax error」というメッセージのエラーが投げられる', () => {
let foo = () => {
throw new TypeError('type error');
return 'foo';
}
expect(foo).toThrowError(SyntaxError, 'syntax error'); // => FAILED
});
使用例4
it('foo関数から正規表現にマッチしたエラーメッセージが投げられる', () => {
let foo = () => {
throw new SyntaxError('syntax error');
return 'foo';
}
expect(foo).toThrowError(/^syntax/); // => SUCCESS
});
it('foo関数から正規表現にマッチしたエラーメッセージが投げられる', () => {
let foo = () => {
throw new SyntaxError('syntax error');
return 'foo';
}
expect(foo).toThrowError(/syntax$/); // => FAILED
});
使用例5
it('foo関数からSyntaxErrorという名前で正規表現にマッチしたメッセージのエラーが投げられる', () => {
let foo = () => {
throw new SyntaxError('syntax error');
return 'foo';
}
expect(foo).toThrowError(SyntaxError, /^syntax/); // => SUCCESS
});
it('foo関数からSyntaxErrorという名前で正規表現にマッチしたメッセージのエラーが投げられる', () => {
let foo = () => {
throw new TypeError('type error');
return 'foo';
}
expect(foo).toThrowError(SyntaxError, /syntax$/); // => FAILED
});
.notメソッドについて
Jasmineでは、.notメソッドが用意されていて、否定評価をすることができるようになっています。.notメソッドをつけてexpect()メソッドを呼び出し、matcherを呼び出すと、ネガティブにアサーションが評価されます。状況によっては、.notメソッドをつけた方が評価しやすい場合もあります。
使用例
it('aはbと厳密等価である', () => {
let [a, b] = [30, 30];
expect(a).toBe(b); // => SUCCESS
});
it('aはbと厳密等価ではない', () => {
let [a, b] = [30, 30];
expect(a).not.toBe(b); // => FAILED
});
it('aはbと厳密等価ではない', () => {
let [a, b] = [30, '30'];
expect(a).not.toBe(b); // => SUCCESS
});
まとめ
Angular CLIでAngularプロジェクトを作成すると、Jasmineが一緒にインストールされるので、Angularでテストを書く場合はJasmineは必須の知識となってきます。まずは、今回紹介したmatcherを覚えておけば、ある程度のテストには対応できるかと思います。その後にスパイやモックの使い方や、さまざまなテストケースに対応できる書き方を覚えていけばよいでしょう。
コメント