■JUnit
特徴
-
・自動化
-
JUnitはテストを自動実行するだけでなく、テスト結果も自動的にチェックする。
-
結果はビジュアル的に分かり易く、エラー時は間違った箇所を指摘してくれる。
-
・容易なテストの記述
-
テスト用のクラスライブラリが充実しているのでテスト用コードを手早く書ける。
-
・テストケースの階層化
-
複数のテストケースをグループ化することができ、更にそのグループもグループ化できるために全体をツリー型に構成して全体を分かり易く整理することができる。
構造
・インターフェイス
インターフェイス
|
説明
|
junit.framework.Protectable
|
保護された環境で実行できる
|
junit.framework.Test
|
テストのクラスで実際のテストを実行し結果を収集する
|
junit.framework.TestListener
|
テスト進行のためのリスタ
|
・クラス
クラス
|
説明
|
junit.framework.Assert
|
テスト結果を判定するクラス
|
junit.framework.TestCase
|
複数のテストを実行するクラス
このクラスで複数のテストケースを定義する
|
junit.framework.TestFailure
|
テストエラーのクラス
このクラスのインスタンスが1つのエラーに対して1つ作成される
エラー番号・説明・ソース情報・トレースメッセージを管理する
|
junit.framework.TestResult
|
テスト結果のクラス
実行したテストの数と発生したエラー情報、失敗したテスト情報を管理する
|
junit.framework.TestSuite
|
複数のテストを管理するクラス
複数のテストを1つのグループとして扱うときに使用する
|
・クラス
クラス
|
説明
|
junit.framework.Assertion.FailedError
|
アサート時にスローする
|
インストール
・入手先
JUnitのWebサイト(http://www.junit.org/)から最新のzipファイル(2005.2現在:junit3.8.1.zip)をダウンロードします。
・設定
ダウンロードしたzipファイルを任意のフォルダに展開後、環境変数(クラスパス)にそのパスを追加します。
以下の説明では「C:\junit3.8」に展開したものとしています。
Windows95/98の場合
autoexec.batをエディタで編集し「classpath C:\junit3.8\junit.jar」を追加します。
WindowsNTの場合
「システムのプロパティ」ダイアログボックスの「環境」タブを選択する。
「変数:」欄に環境変数として「classpath」を入力し、「値:」欄に「C:\junit3.8\junit.jar」を入力します。
既にclasspath変数が登録されている場合は、パスを追加します。
Windows2000の場合
「システムのプロパティ」ダイアログボックスの「詳細」タブを選択します。
「環境変数:」ボタンをクリックしユーザー環境変数欄に「classpath」を登録します。
(登録内容はWindowsNTの場合と同じ)
尚、一時的に環境変数へパスを設定する場合はsetコマンドを使用します。
動作確認
JUnitに付属するサンプルプログラムをTestRunnerで動作させセットアップが正しく完了したか確認します。
CUI版TestRunnerでの確認
prompt>java junit.textui.TestRunner junit.samples.AllTests
AWT版TestRunnerでの確認
prompt>java junit.awtui.TestRunner junit.samples.AllTests
Swing版TestRunnerでの確認
prompt>java junit.swingui.TestRunner junit.samples.AllTests
ファイル構成
フォルダ/ファイル
|
説明
|
doc/
|
各種ドキュメント
|
javadoc/
|
APIリファレンス(JavaDoc形式)
|
junit/
|
JUnitのサンプル及びソースコード
|
junit.jar
|
JUnitテスティングフレームワーク及びツール
|
src.jar
|
JUnitテスティングフレームワークのソースコード
|
readme.html
|
Readmeドキュメント
|
テストプログラム作成手順
説明のために簡単なサンプルクラスを作成します。
-
クラス名:SampleForJunitTest
-
コンストラクタ:SampleForJunitTest (int iVal);
-
引数で渡された値を保持します。
-
引数のチェックは行いません。
-
メソッド:int getValue()
-
現在保持している値を返します。
-
メソッド:int doAddition(int iVal) throws IllegalArgumentException
-
現在保持している値に引数で渡された値を加算した結果を返します。
結果はprivateなフィールドとして保持します。
引数として負の値が渡されたときはIllegalArgumentExceptionを投げます。
-
publicフィールド:なし
-
テストケース1:コンストラクタのテスト
-
確認事項:引数で渡された値がprivateなフィールドとして保持されていること
-
テストケース2:アクセサのテスト
-
確認事項:getterメソッドがフィールドに保持されている値を正しく返すこと
-
テストケース3:演算処理テスト
-
確認事項:演算結果が正しく返されること
-
テストケース4:例外テスト
-
確認事項:引数に負の値を渡した時に例外が発生すること
① テスト対象クラスのスケルトン作成
package jp.co.itd.junit_sample;
public class SampleForJunitTest {
private int _iValue; // 計算結果を保持するprivateフィールド
public SampleForJunitTest(int iVal) {
}
public int getValue() {
}
public int doAddition(int iVal) {
}
}
テストファーストと言ってもインタフェース部分は作成しておかないとテストプログラムのコンパイルも通らないのでクラス図に基づき最低限の定義はしておきます。
② テストコード記述
テストケース1用
import junit.framework.*;
import jp.co.itd.junit_sample.*;
public class TestConstructor extends TestCase
{
private SampleForJunitTest obj;
// コンストラクタ
public TestConstructor(String name) { Super(name); }
// 初期設定
protected void setUp() { obj = new SampleForJunitTest(10); }
// テスト実行
public void runTest() {
testSampleConstructor();
}
// 終了処理
public void tearDown() { }
// コンストラクタのテスト
public void testSampleConstructor()
{
Class objClass = obj.getClass(); // Class オブジェクト取得
Field objField = objClass.getDeclaredField("_iValue "); // Field オブジェクト取得
objField.setAccessible(true); // フィールドをアクセス可能に
assertEquals("_iValue", objField.getInt(obj),10); // 仕様通りなら_iValueが10の筈
}
}
テストケース2用
import junit.framework.*;
import jp.co.itd.junit_sample.*;
public class TestAccessor extends TestCase
{
private SampleForJunitTest obj;
public TestAccessor (String name) { Super(name); }
protected void setUp() { obj = new SampleForJunitTest(20); }
public void runTest() {
testGetValue();
}
public void tearDown() {}
// getterメソッドのテスト
public void testGetValue()
{
assertEquals("getValue", obj.getValue(),20);
}
}
テストケース3用
import junit.framework.*;
import jp.co.itd.junit_sample.*;
public class TestOperator extends TestCase
{
private SampleForJunitTest obj;
public TestOperator (String name) { Super(name); }
protected void setUp() { obj = new SampleForJunitTest(50); }
public void runTest() {
testDoAddition();
testSaveValue();
}
public void tearDown() {}
// 演算処理のテスト
public void testDoAddition()
{
assertEquals("doAddition", obj. doAddition (40),70);
}
// データ保持の確認
public void testSaveValue()
{
assertEquals("saved value", obj.getValue(),70);
}
}
テストケース4用
import junit.framework.*;
import jp.co.itd.junit_sample.*;
public class TestException extends TestCase
{
private SampleForJunitTest obj;
public TestException (String name) { Super(name); }
protected void setUp() { obj = new SampleForJunitTest(60); }
public void runTest() throws Exception
{
testSampleException();
}
public void tearDown() {}
// 例外処理のテスト
public void testSampleException()
{
try {
obj. doAddition (-80); // わざと例外発生
fail("IllegalArgumentException発生していない");
} catch (IllegalArgumentException ie) {
} catch (Exception e) {
fail("別の例外が発生してしまった");
}
}
}
③ テストケースのグループ化
テストケース1~4を一度にテストできる様にまとめます。
import junit.framework.*;
import jp.co.itd.junit_sample.*;
public class TestJunitSample extends TestSuite
{
public TestJunitSample (String name) { Super(name); }
public static TestSuite suite() {
TestSuite testSuite = new TestJunitSample ("JUnit Test Sample");
testSuite.addTest(new TestConstructor ("Constructor test"));
testSuite.addTest(new TestAccessor ("Accessor test"));
testSuite.addTest(new TestOperator ("Operator test"));
testSuite.addTest(new TestException ("Exception test"));
return testSuite;
}
}
④ テスト実行
今作成したテスト用プログラムをコンパイルして実行してみます。
メソッド内部は何も実装されていないので当然全てエラーとなる筈です。
この最初にエラーになるのを確認するのがテストファーストの流儀。
⑤ メソッドの実装とテストの繰り返し
手順4で報告されたエラーに対し、1件ずつ機能を実装させて解消します。最終的にエラーが報告されなくなればコーディング完了です。
この時、まとめてコーディングを済ませてからテストを実行するのではなく、1つ実装してはテストを実行するという具合にできるだけ短い間隔でテストを繰り返すのがコツ。
今まで正常に終了していたテストケースが急にエラーになったら、前回テスト実行後変更したところに原因があります。テスト間隔が短いほど範囲が限定されバグの特定が容易になります。
このように常にデグレードを監視していれば、品質は格段に向上します。
|