トラックバックしていただいたブログをみて、思い出しました!!
(トラックバックしていただき、ありがとうございますです)
そうです。そのブログに書かれているとおり、ドライバ作って、そこからメソッドを引数を変えて呼び出すだけでは、できないテストが、あります。
とくに、想定外の例外が発生したときの異常系っていうのが、思わぬバグがおきたときに大事なので、テストする必要があります。
で、こういうののテストのためには、呼び出される側のスタブを作る必要があるのですが、そのスタブを作るときに、問題があるんです。
たとえば、このクラスをテストするとしますよね。
(yd.readDataが、上記の呼び出される側=スタブが必要)
さあ、テストするドライバを作りましょう!
っていうことで、
っていうテストドライバをつくっちゃうと、
yobidashiクラスのreadDataメソッドは、
return 1;
として、まず、テストを行い、それがOKだったら、今度は
return 2;
に修正して、それもOKだったら
return 3;
に修正して、それもOKだったら
Exceptionをスローするように修正して。。。
と、大変なのよ!
(今は、3個だから、ぜんぜん大変じゃないけど、ウィリアムのいたずらの作るプログラムなんかだと、このケースが200個以上とかあるものもあって、大変なのよ!)
だから、テストドライバからスタブに、どの値をセットするかを指示しないといけない。。
このテストしようとしているChkCtl1クラスを修正しないで!!
そんなとき、どうするか。
ウィリアムのいたずらの場合、こういう、テストケースの値を持っているクラスを用意してます。
staticにしておくことがみそです!
で、ドライバ側は、テスト番号を順番に切り替えて、テストしていくわけ
こんなかんじ
StaticのtestNoの値を変えているので、他のところから呼び出されても、ちゃんと現在のセットされた値を返すわけだ(うーん、うまく表現できん)
で、スタブ側は、テスト番号に応じて、値を返したり、値をセットしたり、Exceptionをスローする。
とくに、想定外のエラーをとるためにExceptionで受け取っている場合っていうのがありますよね。その場合、RuntimeExceptionなら、throwsのところに書いてなくても、スローできるという特性を、case 5:に使っています。
こうすると、テストドライバから流しただけで、ログ(今回はSystem.out.printlnだけど)がとれて、こんな形で、きれいにエビデンスがとれます。
で、テスト仕様書に書く項目ですが、
あるメソッド内部で呼び出して、
その結果をもとに処理が変化する
といった今回のような場合では、変化するケースぶん、テストをつくることになります。
で、テストのしかたは、
・テスト番号を持っているクラス(例だとImaDoko)を作成し、
・ドライバからは、そのテスト番号を変えていく。
・スタブでは、そのテスト番号にあった、帰り値その他もろもろを返す。
・テスト仕様書には、そのテスト番号のところに、スタブでどういう値を返すから、結果、なになにが、動くはず!みたいに書く。
・もし、なんだったら、
テスト仕様書のフリーフォーマットで書く欄に表を作成
その表に、
テスト番号、
スタブで返す値、
その他、スタブで設定する値
起動するメソッド(など期待する処理)
を書いておくと、自動化できるし、そもそも、テスト仕様書が見やすい&書きやすい。
てなかんじでやってます。
(トラックバックしていただき、ありがとうございますです)
そうです。そのブログに書かれているとおり、ドライバ作って、そこからメソッドを引数を変えて呼び出すだけでは、できないテストが、あります。
1.メソッドの内部から、他のメソッドを呼び出し、その呼び出しメソッドの帰り値(または引数)を参照して、他のプログラムを起動する場合。引数だけでは制御できないというか、引数がまったくないときがある。 |
2.DBのデッドロック等、なんらかの例外が発生したときの異常系 |
とくに、想定外の例外が発生したときの異常系っていうのが、思わぬバグがおきたときに大事なので、テストする必要があります。
で、こういうののテストのためには、呼び出される側のスタブを作る必要があるのですが、そのスタブを作るときに、問題があるんです。
たとえば、このクラスをテストするとしますよね。
(yd.readDataが、上記の呼び出される側=スタブが必要)
import java.sql.SQLException; import java.util.*; public class ChkCtl1 { public int doTheJob() { HashMap retHash = new HashMap(); Job job = new Job(); try { // このYobidashiの結果で、処理内容がきまるとき // YobidashiをここでNewして Yobidashi yd = new Yobidashi(); switch(yd.readData(retHash)) { // そのとってきた値を元に、 // モデルのクラスをディスパッチしてる場合 case 1: System.out.println("1" + retHash); return job.Job1(retHash); case 2: System.out.println("2" + retHash); return job.Job2(retHash); case 3: System.out.println("3" + retHash); return job.Job3(retHash); default: return -1; } } // SQLExceptionがおきることは、想定している catch(SQLException e) { System.out.println("SQLException"); return -99; } // 想定外のエラー対応 catch(Exception e) { System.out.println(e); return -999; } } } |
さあ、テストするドライバを作りましょう!
っていうことで、
public class TestDriver { public static void main(String[] args) { ChkCtl1 chk = new ChkCtl1(); System.out.println("start"); chk.doTheJob(); System.out.println("end"); } } |
っていうテストドライバをつくっちゃうと、
yobidashiクラスのreadDataメソッドは、
return 1;
として、まず、テストを行い、それがOKだったら、今度は
return 2;
に修正して、それもOKだったら
return 3;
に修正して、それもOKだったら
Exceptionをスローするように修正して。。。
と、大変なのよ!
(今は、3個だから、ぜんぜん大変じゃないけど、ウィリアムのいたずらの作るプログラムなんかだと、このケースが200個以上とかあるものもあって、大変なのよ!)
だから、テストドライバからスタブに、どの値をセットするかを指示しないといけない。。
このテストしようとしているChkCtl1クラスを修正しないで!!
そんなとき、どうするか。
ウィリアムのいたずらの場合、こういう、テストケースの値を持っているクラスを用意してます。
staticにしておくことがみそです!
public class ImaDoko { /** 現在のテスト番号 */ public static int testNo = 0; /** * セッターだよ。ついでに、セットされたテスト番号も表示 */ public static void setTestNo(int no) { testNo = no; System.out.println("********** test NO = " + testNo + " ***********"); } /** * ゲッターだよ。 */ public static int getTestNo() { return testNo; } } |
で、ドライバ側は、テスト番号を順番に切り替えて、テストしていくわけ
こんなかんじ
public class TestDriver { public static void main(String[] args) { ChkCtl1 chk = new ChkCtl1(); System.out.println("start"); ImaDoko.setTestNo(1); chk.doTheJob(); ImaDoko.setTestNo(2); chk.doTheJob(); ImaDoko.setTestNo(3); chk.doTheJob(); ImaDoko.setTestNo(4); chk.doTheJob(); ImaDoko.setTestNo(5); chk.doTheJob(); System.out.println("End"); } } |
StaticのtestNoの値を変えているので、他のところから呼び出されても、ちゃんと現在のセットされた値を返すわけだ(うーん、うまく表現できん)
で、スタブ側は、テスト番号に応じて、値を返したり、値をセットしたり、Exceptionをスローする。
/** * スタブだよ */ public class Yobidashi { public int readData(HashMap ret) throws SQLException { switch(ImaDoko.getTestNo()) { case 1: ret.clear(); ret.put("komoku1","1"); ret.put("komoku2","2"); ret.put("komoku3","3"); return 1; case 2: ret.clear(); ret.put("komoku1","12"); ret.put("komoku2","22"); ret.put("komoku3","32"); return 2; case 3: ret.clear(); ret.put("komoku1","31"); ret.put("komoku2","32"); ret.put("komoku3","33"); return 3; case 4: SQLException se = new SQLException(); throw se; case 5: RuntimeException e = new RuntimeException(); throw e; } return 99; } } |
とくに、想定外のエラーをとるためにExceptionで受け取っている場合っていうのがありますよね。その場合、RuntimeExceptionなら、throwsのところに書いてなくても、スローできるという特性を、case 5:に使っています。
こうすると、テストドライバから流しただけで、ログ(今回はSystem.out.printlnだけど)がとれて、こんな形で、きれいにエビデンスがとれます。
start ********** test NO = 1 *********** 1{komoku3=3, komoku1=1, komoku2=2} ********** test NO = 2 *********** 2{komoku3=32, komoku1=12, komoku2=22} ********** test NO = 3 *********** 3{komoku3=33, komoku1=31, komoku2=32} ********** test NO = 4 *********** SQLException ********** test NO = 5 *********** java.lang.RuntimeException End |
で、テスト仕様書に書く項目ですが、
あるメソッド内部で呼び出して、
その結果をもとに処理が変化する
といった今回のような場合では、変化するケースぶん、テストをつくることになります。
で、テストのしかたは、
・テスト番号を持っているクラス(例だとImaDoko)を作成し、
・ドライバからは、そのテスト番号を変えていく。
・スタブでは、そのテスト番号にあった、帰り値その他もろもろを返す。
・テスト仕様書には、そのテスト番号のところに、スタブでどういう値を返すから、結果、なになにが、動くはず!みたいに書く。
・もし、なんだったら、
テスト仕様書のフリーフォーマットで書く欄に表を作成
その表に、
テスト番号、
スタブで返す値、
その他、スタブで設定する値
起動するメソッド(など期待する処理)
を書いておくと、自動化できるし、そもそも、テスト仕様書が見やすい&書きやすい。
てなかんじでやってます。