Template Methodパターンの事例
フレームワークを設計する
次に、フレームワークを実現するための設計を行います。
1.テストケースの実行順序を統一したい
2.具体的なテストケースの処理内容のみ実装したい
テストケースの実行順序は、すべて同じ処理の流れになります。一方、テストケースの内容は固有の処理になります。前者を抽象クラス、後者を具象クラスとして実装し、継承を用いることで実現できます。
下の実装例Aのように、同じ処理を抽象クラスとして実装しておきます。抽象クラスを継承して、具象クラスに必要な処理を実装するようにすれば、必要最低限の実装で済むことになります。
テスト前処理やテスト後処理のように、具象クラスでも具体的な処理があるかわからない場合は、(1)のように空実装しておきます。空実装にしておけば、具象クラスで必要に応じてオーバーライドして処理内容を変更することができます。
逆にテストケース実行処理のように、具象クラスで必ずオーバーライドして処理を実装してもらいたい場合は、(2)のようにabstract修飾子を付けておきます。
===================================
【実装例A】
// 抽象クラス
class abstract TestCode {
public final void run() {
setUp(); // テスト前処理
testCase(); // テストケース実行処理
tearDown(); // テスト後処理
}
protected void setUp() {} ... (1)
protected abstract void testCase(); ... (2)
protected void tearDown() {} ... (1)
}
// 具象クラス
class xxxTestCode extends TestCode {
protected void setUp() {
// 必要があれば、その処理内容を実装する
}
protected void testCase() {
// 具体的なテストケースの処理内容を実装する
}
protected void tearDown() {
// 必要があれば、その処理内容を実装する
}
}
===================================
3.テスト結果を評価する共通処理をすべてのテストコードから利用できるようにしたい
抽象クラスに共通処理を実装することで具象クラスから利用できます。
4.テストコードは1つ以上のテストケースで構成される
テストケースの実装は、抽象クラスのTestCaseメソッドをオーバーライドして実現するつもりでした。しかし、それでは図2のBeforeのように、テストコードに1つのテストケースしか実装することができません。
そこで、少し視点を変えて考えます。
図2のAfterのように、テストを実施するときにテストケース名を指定して実行でき
るようにします。そうすれば、具象クラスにテストケースが何件あっても対応できます。
フレームワークの完成
いよいよ、デザインパターンを用いてフレームワークを完成させます。
■デザインパターンの適用
Template Methodパターンを適用する場合、次のような実装を行うことになります。
・テンプレートメソッドを持った抽象クラスを実装する
・テンプレートメソッド内で呼び出しているメソッドは抽象メソッドとして実装される、あるいは具象クラスでオーバーライド対象になるものが多い
・共通処理の実装は抽象クラスで行う
・抽象クラスを継承した具象クラスは固有の実装のみ行う
===================================
【実装例B】
// 抽象クラス
public abstract class TestCode {
private String methodName;
public TestCode(String methodName) { ... }
// テンプレート・メソッド
public final void run() { ... (1)
setUp(); // テスト前処理
invokeTestCase(); // テストケース実行処理
tearDown(); // テスト後処理
}
protected void setUp() {}
protected void tearDown() {}
private void invokeTestCase() { ... (4)
// methodNameで指定した名前の
// メソッドを間接的に実行するための実装を行う
}
// テスト結果を評価する共通処理
protected void assertTrue( ... ) { ... } ... (3)
protected void assertEquals( ..., ...) { ... } ... (3)
}
// 具象クラス
public class ArrayListTestCode extends TestCode {
ArrayList list;
public ArrayListTestCode(String methodName) { ... }
protected void setUp() { ... (2)
ArrayList list = new ArrayList();
}
public void testIsEmpty() { ... (2)(4)
assertTrue(list.isEmpty()); ... (3)
}
public void testSize() { ... (2)(4)
list.add(new Object());
assertEquals(list.size(), 1); ... (3)
}
}
===================================
■デザインパターンの効果
今回の事例では、Template Methodパターンを適用することで、次の特徴を持ったフレームワークを作成することができました(上の実装例Bを参照してください)。
(1)テスト実行時の実行順序を統一する
(2)テストケースに必要な処理の実装のみを行う
(3)テスト時に利用する共通処理をすべてのテストコードで利用できる
また、テスト時に実行したいテストケース名を指定してもらう仕様にしたことで、(4)のように1つのテストコードに複数のテストケースが実装できるようになりました。
===================================
【利用イメージ】
TestCode test;
test = new xxxTestCode("testIsEmpty");
test.run();
test = new xxxTestCode("testSize");
test.run();
===================================