辞書を片手に~PatternResponderの作成(8)
「辞書を片手に~辞書はファイルに(5) 」でRandomResponderにおけるランダムに応答を返す処理のテストを実施しない言い訳が書いてある。
あの時点ではその言い訳が通ったが、そろそろ無理が出てきたということだろう。
なぜか、RandomResponderのテストはFileReaderTestになっている。最初からランダムのテストをしない気で満々であるのが良く分かる。
実際にはFileを扱う部分(loadDictionary)はResponderに移動してしまったし、assertEquals(String[],String[])も2箇所で定義されていたりして煩わしい。
ランダムのテストを追加する前に、テストケースのリファクタリングに取り掛かる。
まずは、FileReaderTestをResponderTestとRandomResponderTestに分割する。
手順は、
1 FileReaderTestをRandomResponderTestにリネーム
2 空状態のResponderTestを作成
3 RandomResponderTestとPatternResponderTestをResponderTestから継承するように変更
4 assertEquals(String[],String[])とtestLoadDictionary()をpull up(この順番で無いとコンパイルエラーが発生する)
5 PatternResponderTestのtestLoadDictionary()がオーバライド扱いになってしまうので、testLoadDictionaryPattern()にリネーム
続いて、RandomResponderTestにテストを追加する。
確実に失敗するとは思うが、random.txtの応答を順番に返すテストをしてみる。
public void testRandomResponse(){
RandomResponder rr = new RandomResponder("random");
assertEquals("今日はさむいね", rr.response("1番目の応答"));
assertEquals("チョコたべたい", rr.response("2番目の応答"));
assertEquals("きのう10円ひろった", rr.response("3番目の応答"));
}
テスト実行。当然失敗である。
RandomResponderがどのように応答をランダムに選んでいるのかというと、java.util.Randomクラスを使用している。
このクラスの代わりに、『Randomクラスのように振舞うが予め決められた値を返すクラス』を使用するようにして、テストと本番に応じて使用するRandomクラスを切り替えればよい。
まずは、Randomクラスを継承した偽Randomクラスを作る。
public class FakeRandomInt extends Random {
}
次にRandomResponderがRandomクラスのインスタンスをコンストラクタで受け取れるようにする。
public class RandomResponder extends Responder {
Random rnd;
public RandomResponder(String name) {
this(name,"random.txt",new Random());
}
public RandomResponder(String name,Random rnd) {
this(name,"random.txt",rnd);
}
public RandomResponder(String name,String fileName) {
this(name,fileName,new Random());
}
public RandomResponder(String name,String fileName,Random rnd) {
super(name);
this.rnd = rnd;
try {
resps = loadDictionary(new FileReader("dics/"+fileName));
} catch (FileNotFoundException e) {
resps = new String[]{""};
}
}
....
}
下線部がRandomに関する修正である。
そして、先ほどのテストでRandomResponderのインスタンス生成時に偽Randomクラスを渡すようにする。
public void testRandomResponse(){
RandomResponder rr = new RandomResponder("random",new FakeRandomInt());
assertEquals("今日はさむいね", rr.response("1番目の応答"));
assertEquals("チョコたべたい", rr.response("2番目の応答"));
assertEquals("きのう10円ひろった", rr.response("3番目の応答"));
}
ここまででテスト実行。Red。やはり状況は変わらない。FakeRandomIntがRandomから何も変更していないためだ。
RandomRespondeで使用しているRandomのI/FはnextInt(int)だけである。当面、これをオーバーライドするだけで十分だろう。
とりあえず、かならず0を返すように実装してみる。
public class FakeRandomInt extends Random {
public int nextInt(int arg0) {
return 0;
}
}
テスト実行。1番目の応答のテストは通るようになった。
このテストに使用するだけなら、nextInt(int)は指定した範囲で0から順番に値を返せば十分に思える。
そのように修正する。
public class FakeRandomInt extends Random {
private int fakeValue = 0;
public int nextInt(int arg0) {
return (fakeValue++) % arg0;
}
}
テスト実行。Green。OK、これでランダムを制御する道具を手に入れた。