標準出力のテスト(結城さんのJUnitのエントリに反応してみる)

結城さんのエントリで意見を募集されているようなので、ちょっとだけ違うやり方を書いてみます。私自身、久しぶりのテストネタです。


その前に、動作報告から。

これってEclipseでも使えるんでしょうか?(まだ試してない)

結城さんのテストはEclipseでも動作いたしました。なお、JUnit 3.8.1のjunit.swingui.TestRunnerはまだ試せていません。


さて、結城さんのテストのお題に関しては、改行がテスト対象なのか否かでテストの設計が変わってくるのではないでしょうか。もし改行コードがテストの意図でないのならばこうも書けますよ、という視点からスタブベースのテストを書いてみます。アプローチは結城さんと同じくSystem.outの入れ替えです。
また、結城さんは「標準出力のテストはこれで妥当でしょうか?」と仰っていますが、Javaでは結局結城さんのやり方が無難だと思います。ちなみにRubyではmasarlさんのAcceptance Test Firstが思い出されます。


では、スタブのアプローチはこんな感じ、という視点で読んでみてください。

package com.example.hello;

import static org.junit.Assert.assertEquals;

import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import junit.framework.JUnit4TestAdapter;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;


public class TestHelloByStub {
    private PrintStream _out;

    @Before public void setUp() {
        _out = System.out;
    }

    @After public void tearDown() {
        System.setOut(_out);
    }

    @Test public void 標準出力に2行出力すること() throws Exception {
        // Arrange
        final List actual = new ArrayList();
        PrintStream fakePrintStream = new PrintStream("ファイル名はここではどうでもいい") {
            @Override
            public void println(String str) {
                actual.add(str);
            }
        };
        
        // Act
        System.setOut(fakePrintStream);
        Hello.main(new String[0]);
        
        // Assert
        List expected = Arrays.asList("Hello!", "http://www.hyuki.com/");
        assertEquals(expected, actual);
    }
    
    public static junit.framework.Test suite() {
        return new JUnit4TestAdapter(TestHelloByStub.class);
    }
}

スタブを使って出力内容をテストするという視点から書いてみました。部分によっては少々雑ですね…。Java5でプログラムを書くのは久しぶりなので、ところどころ変かもしれません。あと、OutputStreamはinterfaceだとさっきまで思っていました(ちょっとびっくりしました)。


さて、このテストにはモック/スタブベースのテストの短所も出ています。テスト対象の実装との結合度がかなり高いのです。つまり、私のテストのほうがテストとしての資産価値が低いということになります。


(追記) 結城さんに合わせてHello.main(new String[0])に変更しました