1.単体テストとは
本記事はJunit4.0で検証しています。
Javaの場合、単体テストとはクラス単位のテストのことです。
単体テストはクラスを作成したプログラマーが自分の責任で行うのが原則です。
今回はテスト対象クラスとして以下を用意しました。
学生の成績を付けるのに以下のような4段階評価をするプログラムです。
S:100点~90点
A:89点~80点
B:79点~70点
C:69点~0点
なお、点数は0または正の整数であり、実数ではないものとする。
<ソースコード①>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
package junittest; public class Score { public String judgeGrade(int score) { if (score < 0) { throw new IllegalArgumentException("score は 0点以上です。:" + score); } else if (score > 100) { throw new IllegalArgumentException("score は 100点以下です。:" + score); } if (score < 70) { return "C"; } else if (score < 80) { return "B"; } else if (score < 90) { return "A"; } else { return "S"; } } } |
2.Junitとは
JUnitとは、Javaで開発されたプログラムにおいてユニットテスト(単体テスト)の自動化を行うためのフレームワークです。(Wikipedia)
当社が提供する研修の環境では、NetBeansにJUnitが最初からインストールされています。
1.JUnitを使ってテストクラスを作成するには、
プロジェクト名を右クリック>新規>既存クラスのテスト を選択すると以下のダイアログボックスが表示されます。
生成するコードでは、必要なコードにチェックを付けます。
今回は、「テスト初期化」と「デフォルトのメソッド本体」を選択します。
2.テストするクラスで先ほどのScore クラスを選び、OKをクリックします。
自動生成されたコードの中で注目したいのが
@Test
というアノテーションです。
この下にテスト用のメソッドを書いていきます。
アノテーションには他にも
@Before
メソッド単位で事前実行します。
オブジェクトの初期化処理などに使います。
など様々あります。
メソッドの中に、
assertEquals(expResult, result);
というメソッドがあります。
expResult:が期待される結果
result:が実際の結果
という意味です。
この両者が一致すればテストOKということになります。
Assertクラスのメソッド
このようなメソッドには、assertEquals()の他に
assertNull();
assertNotNull();
assertTrue();
assertFalse();
などがあります。
では、どのようなテストデータを用意したらよいのでしょうか?
そこで有益な考え方が基本情報技術者試験でも頻出のブラックボックステストの2つの方法。
同値分割と限界値分析の考え方です。
3.同値分割とは
同値分割とは、アウトプットが同じになるような入力をグループにまとめ、グループの中から代表を選んでテストを行なう方式です。今回の4段階評価で言えば、
S:95点
A:85点
B:75点
C:55点
さらにエラー値から-10点と110点を用意するような方法です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
package junittest; import static org.junit.Assert.assertEquals; import org.junit.Before; import org.junit.Test; public class EquivalentDivision { private Score s; @Before public void setUp() throws Exception { s = new Score(); } @Test(expected = IllegalArgumentException.class) public void 引数にマイナス10を渡すとIllegalArgumentExceptionが発生する() { s.judgeGrade(-10); } @Test public void 引数に55を渡すとCを返す() { assertEquals("C", new Score().judgeGrade(55)); } @Test public void 引数に75を渡すとBを返す() { assertEquals("B", new Score().judgeGrade(75)); } @Test public void 引数に85を渡すとAを返す() { assertEquals("B", new Score().judgeGrade(79)); } @Test public void 引数に95を渡すとSを返す() { assertEquals("S", new Score().judgeGrade(95)); } @Test(expected = IllegalArgumentException.class) public void 引数に110を渡すとIllegalArgumentExceptionが発生する() { s.judgeGrade(110); } } |
前述の通り、@Beforeというアノテーションでは、テストの準備に必要な処理を書きます。
また、メソッド名は日本語で書くと以下のようにテスト結果が読みやすくなります。
4.限界値分析とは
限界値分析とは、出力が同じになるような入力をグループにまとめ、グループが隣接する境界やその前後の値を入力としてテストを行なう方式です。今回の4段階評価で言えば、
S:100点と90点
A:89点と80点
B:79点と70点
C:69点と0点
さらにエラー値から-1点と101点を用意するような方法です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
package junittest; import static org.junit.Assert.assertEquals; import org.junit.Before; import org.junit.Test; public class EquivalentDivision { private Score s; @Before public void setUp() throws Exception { s = new Score(); } @Test(expected = IllegalArgumentException.class) public void 引数にマイナス10を渡すとIllegalArgumentExceptionが発生する() { s.judgeGrade(-10); } @Test public void 引数に55を渡すとCを返す() { assertEquals("C", new Score().judgeGrade(55)); } @Test public void 引数に75を渡すとBを返す() { assertEquals("B", new Score().judgeGrade(75)); } @Test public void 引数に85を渡すとAを返す() { assertEquals("B", new Score().judgeGrade(79)); } @Test public void 引数に95を渡すとSを返す() { assertEquals("S", new Score().judgeGrade(95)); } @Test(expected = IllegalArgumentException.class) public void 引数に110を渡すとIllegalArgumentExceptionが発生する() { s.judgeGrade(110); } } |
5.ホワイトボックステストとは
ホワイトボックステストはブラックボックステストとは異なり、クラスの内部構造を考慮に入れたテストです。
その中でも複合条件網羅の考え方が重要になりますので、ここで試してみましょう。
基本情報技術者平成20年春期 午前問48を例にとって説明します。
まず、問題にある判定条件網羅(分岐網羅)についてみていきます。
分岐は2つYesとNoです。
Yesのケース(A=5,B=0)で満たしています。
Noのケース (A=4,B=1)で満たしています。
次に複合条件網羅について考えます。
条件は2つ、A>6、B=0です。
なので、
①A>6がTrue、B=0がTrue
②A>6がTrue、B=0がFalse
③A>6がFalse、B=0がTrue
④A>6がFalse、B=0がFalse
の4パターンをテストしなければいけません。
このうち、
③A>6がFalse、B=0がTrue は 問題文中の判定条件網羅の(A=5,B=0)が満たしています。
④A>6がFalse、B=0がFalse は 問題文中の判定条件網羅の(A=4,B=1)が満たしています。
よってあとは、
①A>6がTrue、B=0がTrue で (A=7,B=0)
②A>6がTrue、B=0がFalse で (A=8,B=2)
を用意すれば複合条件網羅が満たせます。
よって、上記問題の答えは、エ になります。
上記をJunitを使ってテストしてみます。
<ソースコード①>
1 2 3 4 5 6 7 8 9 10 11 12 |
package whiteboxtest; public class WhiteBoxSample { public boolean isOK(int a, int b) { boolean result = false; if (a > 6 || b == 0) { result = true; } return result; } } |
<ソースコード② テストクラス>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
package whiteboxtest; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import org.junit.Before; import org.junit.Test; public class WhiteBoxSampleTest { private WhiteBoxSample wt; @Before public void setUp() throws Exception { wt = new WhiteBoxSample(); } @Test public void 引数に7と0を与えたらTrueが返る() { assertTrue(wt.isOK(7, 0)); } @Test public void 引数に8と2を与えたらTrueが返る() { assertTrue(wt.isOK(8, 2)); } @Test public void 引数に5と0を与えたらTrueが返る() { assertTrue(wt.isOK(5, 0)); } @Test public void 引数に4と1を与えたらFalseが返る() { assertFalse(wt.isOK(4, 1)); } } |
以上、NetBeans でJunitを使った単体テストをする方法を解説しました。
さらに詳細なアノテーションやあさーとメソッドについては以下のマニュアルを参照下さい。