EasyMockを使って標準出力に結果を出すプログラムをテストしてみる

2006-08-18の「標準出力に結果を出すプログラムをJUnit 4.1でテストする方法」と、それに対する反応を見て、ここまで来ると、ほとんどモックオブジェクトを使ったテストなのでは?と思いました。そこで、モックオブジェクトを使ったJunitのテストをサポートするライブラリ、EasyMockを使ってテストケースを書き直してみました。

事前準備

http://www.easymock.org/index.htmlのDownloadsからEasyMock 2.2とEasyMock 2.2 Class Extensionをダウンロード。
次に、http://cglib.sourceforge.net/のDopwnloadsから cglib-nodep-2.2_beta1.jarをダウンロード。これは、EasyMock 2.2 Class Extensionが利用するライブラリ。

テストコード
import junit.framework.TestCase;
import java.io.PrintStream;
import org.easymock.classextension.EasyMock;


public class HelloTest extends TestCase {
    private PrintStream _saved;
    private PrintStream _outMock;
    
    public void setUp() {
        _saved = System.out;
        _outMock = EasyMock.createMock(PrintStream.class); // モックオブジェクト作成
        System.setOut(_outMock);
    }
    
    public void tearDown() {
        System.setOut(_saved);
    }    
    
    public void testHello() {
        // モックオブジェクトに振る舞いを記憶させる
        _outMock.println("Hello!");
        _outMock.println("http://www.hyuki.com/");
        
        // モックオブジェクトをreplay(記憶した振る舞いの再生)モードに切り替え
        EasyMock.replay(_outMock);
        
        // 実行
        Hello.main(new String[0]);
        
        // モックオブジェクトが振る舞い通りに呼ばれたか確認
        EasyMock.verify(_outMock);
    }
}

replay()前のprintln()メソッド呼び出しの引数と、replay()後のprintln()メソッド呼び出しの引数が異なれば、AssertionErrorが投げられます。呼び出すメソッドが異なってもAssetionError。println()メソッド呼び出しの回数が少なければ、EasyMock.verify()がAsserionError。

ちなみに、普通は"import static org.easymock.EasyMock.*;"と書いて、クラス名を省略してEasyMockのstaticメソッド呼び出しを記述するようですが、EasyMockを使っている事を強調するため、あえてクラス名を明記してます。自分がJava 1.5の文法にまだ慣れてない、というのもあるけど。

実行例

eclipse 3.1.2で動作しました。
eclipseに付属のJUnit 3.8.1を直接使う場合は、下記のようなコマンドで。

C:\work>java -classpath ".;easymock.jar;easymockclassextension.jar;cglib-nodep-2.2_beta1.jar;C:\eclipse\plugins\org.junit_3.8.1\junit.jar" junit.textui.TestRunner HelloTest
.
Time: 0.391

OK (1 test)

これでテストコードはすっきりしますが、どうでしょう。依存するライブラリが多い点と、EasyMockの使い方を知らないと(慣れればそんな難しくないけど)テストコードが分かりにくい点が難点かな。
あ、あとt-wadaさんの日記で言及されている、テスト対象の実装との結合度がかなり高いという「モック/スタブベースのテストの短所」が思いっきり出てますね。HelloクラスでSystem.outの使い方(呼び出すメソッドとか)がちょっと変わっただけで(例え、標準出力の結果が同じでも)テストが失敗します。
単に標準出力へ出力する結果が合っていれば良い、というテストであれば、このテストコードは向かないかも。