定数や文字列を外部ファイル化することが、よくあります。
HelloWorldを修正し、propertiesファイルから読み込んだ文字列を
コンソールに表示するプログラムを作成し、そのテストにチャレンジします。
HelloWorldは、多少ブサイクではありますが、下記のようになりました。
01 import java.util.ResourceBundle; 02 03 public class HelloWorld { 04 05 public static void main(String[] args) { 06 ResourceBundle resourceBundle = ResourceBundle.getBundle("HelloWorld"); 07 String message = resourceBundle.getString("KEY"); 08 09 if ("".equals(message)) { 10 System.out.println("Message Not Found."); 11 } else { 12 System.out.println(message); 13 } 14 } 15 16 } |
外部ファイルである、HelloWorld.propertiesは、
と一行書いておきます。
「それらしい」テストにするため、HelloWorldには、あえて分岐を書きました。
ファイルから読み込んだメッセージが、「空文字だったら」という分岐です。
この
HelloWorldのテストは、最低2パターン必要です。
- メッセージが、正常に取得できたとき
- 取得したメッセージが、空文字だったとき
しかし、メッセージを、ファイルから読み込んでいるため、上記2種類のテストを行うには、工夫が必要です。
たとえば、
- HelloWorld.propertiesの内容を書き換える
- propertiesファイルをテストパターンごとに準備し、テスト実行前にHelloWorld.propertiesにリネームする
- 擬似オブジェクトを使用する
など、いろいろありそうです。
ファイルの内容を書き換えたり、ファイル名をリネームしたり、ファイルの関係は、面倒くさそうなので、
擬似オブジェクトを使用して、なんとかしてみます。
擬似オブジェクトを使用するために、HelloWorldを修正します。
考えたあげく、HelloWorldは、こんなんなりました。
01 import java.util.ResourceBundle; 02 03 public class HelloWorld { 04 05 public static void main(String[] args) { 06 HelloWorld world = new HelloWorld(); 07 System.out.println(world.getMessage()); 08 } 09 10 String getMessage() { 11 ResourceBundle resourceBundle = getResourceBundle(); 12 String string = resourceBundle.getString("KEY"); 13 14 return toMessage(string); 15 } 16 17 ResourceBundle getResourceBundle() { 18 return ResourceBundle.getBundle("HelloWorld"); 19 } 20 21 private String toMessage(String message) { 22 if ("".equals(message)) return "Message Not Found."; 23 return message; 24 } 25 26 } |
getResourceBundle()が、ニセモノのResourceBundleオブジェクトを返却するようし、
getMessage()の返却値を検証すればよさそうです。
HelloWorldTestは、このようになりました。
01 import java.util.ListResourceBundle; 02 import java.util.ResourceBundle; 03 04 import junit.framework.TestCase; 05 06 public class HelloWorldTest extends TestCase { 07 08 // 省略 09 10 /** メッセージが、正常に取得できたときのテスト */ 11 public void testMain001() { 12 13 // getResourceBundle()をオーバーライドして、HelloWorldのインスタンスを生成 14 HelloWorld world = new HelloWorld() { 15 ResourceBundle getResourceBundle() { 16 // キーと値を仕込む。 17 // キー : "KEY" 18 // 値 : "Hello World." 19 return new ListResourceBundle() { 20 public Object[][] getContents() { 21 return new Object[][]{{"KEY", "Hello World."}}; 22 } 23 }; 24 } 25 }; 26 27 assertEquals("Hello World.", world.getMessage()); 28 } 29 30 /** 取得したメッセージが、空文字だったときのテスト */ 31 public void testMain002() { 32 33 // getResourceBundle()をオーバーライドして、HelloWorldのインスタンスを生成 34 HelloWorld world = new HelloWorld() { 35 ResourceBundle getResourceBundle() { 36 // キーと値を仕込む。 37 // キー : "KEY" 38 // 値 : "" 39 return new ListResourceBundle() { 40 public Object[][] getContents() { 41 return new Object[][]{{"KEY", ""}}; 42 } 43 }; 44 } 45 }; 46 47 assertEquals("Message Not Found.", world.getMessage()); 48 } 49 } |
疑似オブジェクトによる単体テスト(
http://www-6.ibm.com/jp/developerworks/java/030207/j_j-mocktest.html)
では、このようなアプローチのことを言っているんでしょうか???
- うまくリファクタリングしなければいけないため、プロジェクトメンバ全員が、同じ質のテストを書けそうにない
- テストのパターンが増えたら、擬似オブジェクトの数が多くなり保守が大変そう
- 慣れてないからかもしれませんが、正直いって、えらい疲れました。
djUnitで、チャレンジします。
テスト対象は、最初のままです。
01 import java.util.ResourceBundle; 02 03 public class HelloWorld { 04 05 public static void main(String[] args) { 06 ResourceBundle resourceBundle = ResourceBundle.getBundle("HelloWorld"); 07 String message = resourceBundle.getString("KEY"); 08 09 if ("".equals(message)) { 10 System.out.println("Message Not Found."); 11 } else { 12 System.out.println(message); 13 } 14 } 15 } |
テストクラスは、こうなります。
HelloWorldの7行目、getString(String)の戻り値を、ニセモノに差し替えています。
01 import jp.co.dgic.testing.framework.DJUnitTestCase; 02 03 public class HelloWorldTest extends DJUnitTestCase { 04 05 // 省略 06 07 /** メッセージが、正常に取得できたときのテスト */ 08 public void testMain001() { 09 10 // java.util.ResourceBundle#getStringの返却値として、"Hello World."をセットする 11 addReturnValue("java.util.ResourceBundle", "getString", "Hello World."); 12 13 HelloWorld.main(null); 14 15 assertArgumentPassed("java.io.PrintStream", "println", 0, "Hello World."); 16 } 17 18 /** 取得したメッセージが、空文字だったときのテスト */ 19 public void testMain002() { 20 21 // java.util.ResourceBundle#getStringの返却値として、空文字をセットする 22 addReturnValue("java.util.ResourceBundle", "getString", ""); 23 24 HelloWorld.main(null); 25 26 assertArgumentPassed("java.io.PrintStream", "println", 0, "Message Not Found."); 27 } 28 } |
addReturnValueは、指定したメソッドの返却値を、好みのものに変更します。
void DJUnitTestCase#addReturnValue(String クラス名, String メソッド名, Object 返却値);
djUnitでは、テスト対象のプログラムを修正することなく、また、擬似オブジェクトを作成することもなく、
擬似オブジェクトと同等なテストが可能になりそう。