はじめに
皆さん、テストを書きながら開発を進めていますか?
私が関わっているプロダクトではテストを書きながら開発を進めています。
テストを書く→実装する
を繰り返すことで、プロダクトの信頼性を高めることができる のです。
(たまに実装が先になってしまうこともありますが。。。。)
テストダブルの種類
テストダブルには5つの種類があります。
test Spy
Mock Object
test Stub
Dummy Object
Fake Object
それぞれの種類のイメージを題目に示しながら以下にまとめました。
違いが分かりづらい Spy と Mock
test Spy
テスト対象の間接出力を記録する
「メソッドは呼び出されたかどうか」
「メソッドは何回呼び出されたか」
「メソッドに渡された実引数は何だったか」
というポイントを検証したいときに使用する。
//Test Spyとして動作する代替関数
void 外部メソッド(int input)
{
間接出力値 = input;
}
int テスト対象()
{
...
外部メソッド(x);
...
}
@Test
public void テストコード()
{
テスト対象();
AssertEquals(期待値, 間接出力値);//Test Spyに記録させておいた間接出力値を比較検証する
}
Mock Object
上記のTestSpyは"間接出力値"だけをチェックしているのに対し、
MockObjectは"テスト成功フラグ"を確認することで
1.間接出力値
2.間接出力値によって受け取った結果(true or false)
という2つの事をチェックしている。
一連の確認項目を1つのメソッドで確認するのがMockObjectであるため
TestSpyをリファクタリングした副産物 = MockObject である。
//Mock Objectとして動作する代替関数
void 外部メソッド(int input)
{
if (期待する間接出力値 == input) {
テスト成功フラグ = true;
return;
}
テスト成功フラグ = false;
}
int テスト対象()
{
...
外部メソッド(x);
...
}
@Test
public void テストコード()
{
期待する間接出力値 = 999; //Mock内で比較に用いる期待値
テスト対象();
AssertTrue(テスト成功フラグ); //Mock内で検証した結果をチェック
}
よく使うStub
test Stub
テスト対象に間接入力を操作する
テスト対象に渡したい値を設定するためのオブジェクト
// Test Stubとして動作する代替関数
int 外部メソッド()
{
return indirectInputValue;
}
int テスト対象()
{
...
answer = 外部メソッド();
...
}
@Test
public void テストコード()
{
indirectInputValue = 100; //TestStubである外部メソッド()の返り値となりテスト対象に入力
AssertEquals(期待値, テスト対象());
}
テスト書くたび徐々にわかる?Dummy
Dummy Object
テストに影響を与えない代替オブジェクト
これを設定してもテスト対象の返り値に影響しないが、コンパイルなどの都合上準備すべきもの。
下記は例になりますが、fooの内容が変わってもテスト対象の返り値は必ず10になる、
この時のfooがDummy Object
int テスト対象(Foo foo)
{
return 10;
}
@Test
public void テストコード()
{
Foo foo = new Foo(); //Dummy Object
AssertEquals(期待値, テスト対象(foo));
}
今のところ出番のないFake
Fake Object
テスト実行中に間接出力を受け取り、間接入力を操作するもの
本物同様の動きをする
認証の機能などを持たせたプロダクトで動きを確認するときなどに作る完全コピー品のようなもの。
(まだあんま使った記憶ないな)
結論
test Spy
テスト対象の出力を記録するもの
Mock Object
testSpyをリファクタリングした時の副産物
test Stub
テスト対象に間接入力をする
Dummy Object
テスト対象の結果に影響は与えないけど動かすために必要なもの
Fake Object
本物同様の動きをする、完全コピー品のようなもの
少しは理解ができた気もしますが、
完全理解にはテストを書く訓練がまだまだ必要そうです。