Menu

FrontPage

Eclipseプラグイン
Eclipse小技集
リファクタリング講座
テスティングフレームワーク JUnit
MSDEについて



The 20 newest affair
2010-09-092010-09-082010-09-072010-09-062010-09-012010-08-312010-08-272010-08-242010-08-232010-08-172010-08-162010-07-302010-07-212010-07-132010-06-272010-06-182010-06-172010-06-16
未完成ページ

はじめに

単体テストを行う際に、クラスの依存関係が問題になることが多々あります。
そしてその依存関係のために、

  • テストが面倒になってしまう

ことと、依存関係を断ち切るための方法として

  • 「Mock Object」が有効

であることを書きます。

最終的には、「Virtual Mock Object」が、「Mock Object」の方法を、

  • さらに、シンプルにする

という結論にもっていこうと企んでいます。

クラスの依存関係  Λ


テスト対象クラスとして、2つのクラスを示します。

InfoUser.java
1 public class InfoUser {
2     
3     private PluginInfo info = new PluginInfo();
4     
5     public String getPluginName() {
6         return info.getName();
7     }
8     
9 }

PluginInfo.java
01 import java.util.ResourceBundle;
02 
03 public class PluginInfo {
04     
05     private static final String PROPERTY_FILE_NAME = "info";
06     
07     private static ResourceBundle resource;
08     
09     public PluginInfo() {
10         resource = ResourceBundle.getBundle(PROPERTY_FILE_NAME);
11     }
12     
13     public String getName() {
14         return resource.getString("name");
15     }
16 }

本来であれば、おしゃれにクラス図などを書くべきなのですが、それほどのプログラムでもないので書きません。

とりあえず、

1) InfoUserは、PluginInfoを持っていて、使用している。
2) PluginInfoは、どうやら「info.properties」というファイルを読み込んでいるらしい。

といったところです。

しつこいようですが、
InfoUser#getPluginName()を実行するということは、
PluginInfo#getName()も実行すること
を意味します。

そのままの依存関係でテストする  Λ


これらのクラスをテストしようと考えてみます。
とりあえず、テストクラスを2つ作るんでしょうか。

  • InfoUserTest.java
  • PluginInfoTest.java

あ、info.propertiesも必要です。

info.properties
name=djUnit

とします。

最初に「InfoUserTest」を書いてみます。

InfoUserTest.java
01 import junit.framework.TestCase;
02 
03 public class InfoUserTest extends TestCase {
04 
05     // 省略
06     
07     public void testGetPluginName() throws Exception {
08         InfoUser user = new InfoUser();
09         
10         assertEquals("djUnit", user.getPluginName());
11     }
12 }

こんな風になりました。
このテストは、「InfoUserのテスト」のつもりで作りました。

するってぇと、もうひとつのテストは、

PluginInfoTest.java
01 import junit.framework.TestCase;
02 
03 public class PluginInfoTest extends TestCase {
04 
05     // 省略
06     
07     public void testGetName() throws Exception {
08         PluginInfo info = new PluginInfo();
09         
10         assertEquals("djUnit", info.getName());
11     }
12 }

ってな感じでしょうか。

問題点  Λ


上記テストを作っていて、ちと納得いかないところが出てきました。

  • 「InfoUserTest」と「PluginInfoTest」で、テストの内容が重複している

ということです。
InfoUserのテストは、それぞれPluginInfoを含めたテストをしています。
そして、PluginInfoの全ステップを実行しますので、わざわざ、「PluginInfoTest」を作成しなくても、いいように思います。
(こんな場合、我々は、PluginInfoのテストは書きません。)

あとは、
  • 「InfoUserTest」のために、info.propertiesを準備しなければならない

仕方のないことですが、ここが結構気になります。

検査対象である、InfoUser, PluginInfoは、
なとなくでも、役割によってクラス分けされているはずで、
「ファイル読み込み」に関することは、PluginInfoだけがやっていれば良く、InfoUserは、ファイルとは無関係。
PluginInfoが、ファイル、データベース、通信のどんな手段で文字列を取得しているかは、InfoUserにとっては、どうでもいいお話。
そうすることが責任分担であり、PluginInfoがもし変更になっても、InfoUserには、迷惑をかけにくいということ。

に対して、テストクラスである、InfoUserTestは、
役割ごとのテストに分かれていない。
「ファイル読み込み」に関することは、InfoUserのテストに、モロに関係がある。
PluginInfoが、ファイル、データベース、通信のどんな手段で文字列を取得しているかを把握して、データを準備しなければいけません。
PluginInfoが変更になったら、それを使用しているすべてのInfoUserのテストに、修正がかかる可能性があるということ。

このように、テストを作成する時には、大した問題ではないように思えますが、テストを保守するときには、結構問題となります。

そしてこれは、データベースを使用している場合で、テーブルが修正になってしまったとき、
  • どれだけの「テストクラス」と「テストデータ」の修正が必要か
を考えると、かなり面倒です。

とりあえずの結論  Λ


これを見てきて思うのは、

InfoUserのテストでは、
テストクラス : InfoUserTest
検査対象クラス : InfoUser, PluginInfo

検査対象クラス : テストクラス = 2 : 1

となります。

これを「検査対象クラス : テストクラス = 1 : 1」に持ち込むことができれば、
  • テストの内容が重複する問題
  • テストの保守が困難になる問題
を解決する方向に向かうように思います。


Attached File: [Attached File All List]

Lastmodified: 2004-10-18 (月) 13:42:42 (2152d)