JUnit + JMockit で System.exit(int) と標準入出力の差し替え

まぁ、タイトル通り。今、ちょっと勝手に遊べる環境があるので JUnitJMockit でテストしてみているのだけれど、こいつがウェブアプリではなくコンソールから起動する昔ながらのツールなので、今まではほとんど触らなかった部分の呼び出しも確認したくて困る。

そういうわけで、以下がサンプルコード。なんとなく分かると信じて。

import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintStream;

import mockit.Expectations;
import mockit.Mocked;
import mockit.VerificationsInOrder;

import org.junit.Test;

public class SystemIOTest {

	// System.exit が動くと JUnit ごと終了するので
	// モック化しておく
	@Mocked("exit")
	System s;

	@Test
	public void test_標準入出力を差し替える(@Mocked final PrintStream ps1)
			throws IOException {

		// prepare
		// 標準入力を差し替える
		final ByteArrayInputStream bais = new ByteArrayInputStream(
				"abcdefghijklmnopqrstuvwxyz".getBytes());
		s.setIn(bais);

		// expectations
		new Expectations() {
			{
				// ps1 の #checkError() のみ true を必ず戻す
				onInstance(ps1).checkError();
				result = true;
			}
		};

		// 標準入力からデータを読み込む
		byte[] b = new byte[3];
		assertThat(System.in.read(b, 0, b.length), is(3));
		assertThat(b, is(new byte[] { 'a', 'b', 'c' }));

		// Expectations の影響を受ける PrintStream
		System.out.println("標準出力への出力");
		System.exit(1);
		assertThat(ps1.checkError(), is(true));

		// Expectations の影響を受けない PrintStream
		final PrintStream ps2 = new PrintStream(new ByteArrayOutputStream());
		ps2.println("バイト配列への出力");
		assertThat(ps2.checkError(), is(false));

		// verifications
		new VerificationsInOrder() {
			{
				// 標準出力への出力を確認できる
				ps1.println("標準出力への出力");
				times = 1;

				// System.exit(int) の呼び出しが確認できる
				s.exit(1);
				times = 1;

				// Expectations の影響は受けないが処理はレコードされている
				ps1.println("バイト配列への出力");
				times = 1;
			}
		};

	}

}