テストされるファイル
Jasmineは「○○.spec.ts」なファイル名のファイルを勝手に実行してテストしてくれる。
立派。
describe
テストはこの下に記述する。
第一引数はテスト環境の説明。
第二引数はテストの処理を行うラムダ。
describe
を複数書けば、それぞれ独立したテスト環境でテストできる。
テスト専用の環境を逐一立ち上げて、その中でテストするイメージ。
実際のテストは第二引数で渡したラムダの中で行う。
describe
を入れ子にすることもできる。
例
describe('キャッシュがない場合のテスト', () => {
キャッシュがない状況を作る処理
実際のテスト1
実際のテスト2
実際のテスト3
describe('しかもサーバが死んでるときのテスト', () => {
サーバが死んでる状況を作る処理
実際のテスト1
実際のテスト2
実際のテスト3
});
});
describe('キャッシュがある場合のテスト', () => {
キャッシュがある状況を作る処理
実際のテスト1
実際のテスト2
実際のテスト3
});
なお以降の例ではめんどくさいのでdescribeを書かない。
beforeEach
describe
内でテスト環境を作る関数。
各テスト処理の前に実行されて、いちいち破棄される。
第一引数としてラムダを渡す。この中でテスト環境を作る処理をする。
例
beforeEach(() => {
仮想サーバーを作る処理
});
TestBed
Angularが提供する、テスト環境作成用オブジェクト。
その場でModuleを作ってその下にテスト対象のオブジェクトをぶら下げる的な感じ。
configureTestingModule
TestBed
オブジェクトのメソッド。
テスト対象のオブジェクトが属するモジュールをその場で作り上げる。
第一引数はNGModule
を作るための即時オブジェクト。NGModule
デコレータに渡すやつとほぼ同じ(無いプロパティがあるが)。
コンポーネントのテストするときはdeclarations
に、
サービスのテストするときはprovider
にそのサービスのトークンを記述する。
あとimports
にそのオブジェクトが使うサービスやモジュール記述。
例
beforeEach(() => {
TestBed.configureTestingModule(() => {
// HTTP通信のモックモジュール
imports : [HttpClientTestingModule],
// 今回のテスト対象
providers: [HogeService]
});
});
it
実際のテストを行う関数。
ここが走るとき、beforeEach
に書いた環境構築処理が終了してテスト環境ができあがっている。beforeEach→it→破棄→beforeEach→it→破棄
ってイメージ。
第一引数はテスト内容の説明。it('should be created', () => {})のように、本来は「こうなるはずだ」というのを説明するように記述する。でも日本語でいいやもう。
第二引数はテスト処理を行うラムダ。
expect
it内でつかう、テスト内容が正しいか判断する関数。
この関数だけだと意味がなく、後述するアサート用関数をチェーンして使う。
第一引数はテストしたい値。テスト対象メソッドの戻り値。
アサート用関数
expectの戻り値はMatcherなるオブジェクト。
Matherはアサートのためのメソッド群をもっており、それを呼び出してexpectに渡した引数が条件を満たすか判定する。
以下によく使うやつをまとめる。
-
toBe
引数をexpect
に渡した値と===
で比較し、true
なら成功 -
toEqual
引数をexpect
に渡した値と==
で比較し、true
なら成功 -
toBeTruthy
expect
に渡した値がtrue
なら成功。toEqual(true)
の糖衣 -
toBeFalsy
expect
に渡した値がfalse
なら成功。toEqual(false)
の糖衣
あとtoBeNone
とかtoBeUndefined
とかtoBeGreaterThan
とかtoContain
とかまぁ自然言語に沿う形でいろいろある。
例
it('足し算をする関数が正しい値を返すかテスト' () => {
expect(add(1, 1)).toEqual(2);
});
toThrowError
expect
に渡した「ラムダが」、引数に渡したエラー文字列をもつエラーを投げてくることを判定する。
匿名関数で包んでやらないと普通に実行時エラーになる。
it('小数投げたらエラー' ()=> {
expect(() => addInteger(1.5, 1)).toThrowError('Give me 整数!');
})
無理矢理失敗させる
fail()
を使えば良い。
Promise
を返すメソッドの正常系をテストしたいときに、チェーンしたcatch
に仕込んだりする。
PromiseとかObservableを戻すメソッドのテスト
本来はexpect
の引数でテスト対象メソッドを呼び出す。
ただしPromise
とかを戻すメソッドの場合それでテストできないので、以下のようにする。
hogeObject
のmoge
メソッドの正常系をテストする。戻り値はPromise<String>
とする。
it('mogeの正常系テスト' () => {
hogeObject.moge()
.then(result => expect(result).toEqual('moge!'))
.catch(err=> fail());
});
then
に入ったらPromise
が解決した値をテストする。
うっかりメソッド内で失敗したときのため、catch
に入ったらテスト失敗とする。
spyOn
あるオブジェクトのメソッドを「スパイ」する関数。テスト対象メソッドが依存するメソッドに成り代わる。ただしprivateなやつは無理。
そもそも呼ばれたか、特定の引数で呼ばれたか、などをチェックし、ダミーの値を返すこともできる。
大体以下のメソッドをチェーンして使う。
-
and.callThrough()
メソッド本来の挙動をする。呼ばれたかどうかのチェックするために使う -
and.returnValue(value)
渡した値を返す。テストの独立性を高めるために使う -
and.callFake(rambda)
渡したラムダに従って処理をし、ダミーの値を返す。テストの独立性を高めるために使う
スパイしたメソッドの呼び出しチェック
-
toHaveBeenCalled
expect
に渡した関数が呼ばれていたなら成功 -
toHaveBeenCalledWith
expect
に渡した関数が特定の引数で呼ばれていたなら成功
例
hogeObject
のuge
メソッドをスパイし、ダミーの値2を返す。
その後、呼ばれているかチェックする。
// 生成されるテスト対象オブジェクトの参照を確保
// beforeEach内で実際に確保する。つまりテストごとに書き換えられるのでlet
let hogeObject: HogeService;
// テスト環境生成
beforeEach(() => {
TestBed.configureTestingModule({
// 今回のテスト対象
providers: [HogeService]
});
// 生成されるテスト対象オブジェクトの参照を確保
hogeObject = TestBed.get(HogeService);
});
it('mogeのテスト', () => {
// ugeメソッドをスパイ。
const spy = spyOn(hogeObject, 'uge').and.returnValue(2);
// mogeメソッドをテスト。mogeはugeメソッドを呼び出している
hogeObject.moge()
.then(result => expect(result).toEqual('moge!'))
.catch(err => fail());
// ugeが呼ばれたことのチェック
expect(hogeObject.uge).toHaveBeenCalled();
});
HTTPClientModuleを使うクラスのテスト
AngularでAjax通信する場合、HTTPClient
クラスを使う。
ただしテストでマジに外部と通信するとテストの信頼性が下がるし余所のAPI叩いてると迷惑。
なのでモックサーバをその場で立ち上げる。
モックサーバを使うには、TestBed.configureTestingModule
のimports
にHTTPClientTestingModule
を渡す。
その後、TestBed.get
でHTTPClientTestingController
の参照を変数に格納しておく。
使い方の例
hogeObject
のmoge
メソッドは外部(今回はhttp://localhost/uge/guge
とする)にAjax通信を行う。
let hogeObject: HogeService;
let httpMock: HttpTestingController;
beforeEach(() => {
TestBed.configureTestingModule({
imports : [HttpClientTestingModule],
providers: [HogeService]
});
// 参照を確保する。
// hogeObjectのAjax通信は本当のサーバでなく、HTTPClientTestingModuleが提供する偽サーバになる。
hogeObject = TestBed.get(HogeService);
httpMock = TestBed.get(HttpTestingController);
});
it('hoge正常系' () => {
// ugeメソッドをスパイ。
const spy = spyOn(hogeObject, 'uge').and.returnValue(2);
// mogeメソッドをテスト。mogeはugeメソッドを呼び出している
hogeObject.moge()
.then(result => expect(result).toEqual('moge!'))
.catch(err => fail());
// ugeが呼ばれたことのチェック
expect(hogeObject.uge).toHaveBeenCalled();
// 引数に渡したURLが呼び出された場合のモックを生成&指定したデータを返させる
// なんかよく分からんがこれexpectより後に置かないとエラー吐く
httpMock.expectOne('http://localhost/uge/guge')
.flush({"value": "2"});
// いらんURLが呼び出されてないことのチェック
httpMock.verify();
});