2012年7月12日木曜日

標準出力をJUnitでテストするには

久しぶりにソースを公開してみます。

標準出力に結果を出すプログラムをJUnit 4.1でテストする方法とかで解説されていますが、
それを汎用的にしてみました。

名付けて、StandardOutputSnatcherクラスです。
略してSOS!

import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
/**
* 標準出力・標準エラー出力の出力先を奪うクラスです.
* テストなどでコンソール出力の内容を知りたい場合に使います.
* このクラスはスレッドセーフではありません。
* マルチスレッド環境では利用しないでください。
*/
public final class StandardOutputSnatcher {
/** シングルトンパターンのためのインスタンス. */
private static final StandardOutputSnatcher INSTANCE =
new StandardOutputSnatcher();
/** 元の標準出力. */
private PrintStream nativeOut = null;
/** 元の標準エラー出力. */
private PrintStream nativeErr = null;
/** 変更後の標準出力. */
private ByteArrayOutputStream snatchedOut = new ByteArrayOutputStream();
/** 変更後の標準エラー出力. */
private ByteArrayOutputStream snatchedErr = new ByteArrayOutputStream();
/** 奪取済みフラグ. */
private boolean stealFlag = false;
/** デフォルトコンストラクタの禁止. */
private StandardOutputSnatcher() { }
/**
* このクラスのインスタンスを取得します.
* このクラスはスレッドセーフ化しにくい形で標準出力に干渉するので、
* シングルトンパターンを採用しました.
* @return インスタンス
*/
public static StandardOutputSnatcher getInstance() {
return INSTANCE;
}
/**
* 出力先を奪い、
* コンソールからこのクラスへ出力するように設定します.
*/
public void snatch() {
// ダブルスティールの禁止
if (!stealFlag) {
nativeOut = System.out;
nativeErr = System.err;
System.setOut(new PrintStream(new BufferedOutputStream(snatchedOut)));
System.setErr(new PrintStream(new BufferedOutputStream(snatchedErr)));
stealFlag = true;
}
}
/** 標準出力をクリアします. */
public void clearOutput() {
snatchedOut.reset();
}
/** 標準エラー出力をクリアします. */
public void clearErrorOutput() {
snatchedErr.reset();
}
/**
* 標準出力の出力内容を取得します.
* @return 標準出力の出力内容
*/
public String getOutput() {
System.out.flush();
return snatchedOut.toString();
}
/**
* 標準エラー出力の出力内容を取得します.
* @return 標準エラー出力の出力内容
*/
public String getErrorOutput() {
System.err.flush();
return snatchedErr.toString();
}
/**
* 奪取した標準出力先を取得します.
* @return 奪取した標準出力先
*/
public PrintStream getNativeOutputStream() {
return nativeOut;
}
/**
* 奪取した標準エラー出力先を取得します.
* @return 奪取した標準エラー出力先
*/
public PrintStream getNativeErrorOutputStream() {
return nativeErr;
}
/**
* 出力先を元に戻します.
* このメソッドの呼び忘れにご注意ください.
*/
public void release() {
// ダブルリリースの禁止
if (stealFlag) {
clearOutput();
clearErrorOutput();
System.setOut(nativeOut);
System.setErr(nativeErr);
stealFlag = false;
}
}
}
public class StandardOutputSnatcherSample {
public static void main(String[] args) {
StandardOutputSnatcher sos = StandardOutputSnatcher.getInstance();
System.out.println("開始");
sos.snatch();
// 標準出力
System.out.print("標準出力テスト");
if (sos.getOutput().equals("標準出力テスト")) {
sos.getNativeOutputStream().println("標準出力テスト成功!");
}
// 出力内容のクリア
sos.clearOutput();
// 標準出力(2回目)
System.out.print("標準出力テスト2");
if (sos.getOutput().equals("標準出力テスト2")) {
sos.getNativeOutputStream().println("標準出力テスト2成功!");
}
// 標準エラー出力
sos.clearErrorOutput();
System.err.print("標準エラー出力テスト");
if (sos.getErrorOutput().equals("標準エラー出力テスト")) {
sos.getNativeOutputStream().println("標準エラー出力テスト成功!");
}
// 出力内容のクリア
sos.clearErrorOutput();
// 標準エラー出力(2回目)
System.err.print("標準エラー出力テスト2");
if (sos.getErrorOutput().equals("標準エラー出力テスト2")) {
sos.getNativeOutputStream().println("標準エラー出力テスト2成功!");
}
sos.release();
System.out.println("終了");
}
}

ちなみにLog4jの出力テストにも使えることにお気づきでしょうか?
そうです、ConsoleAppenderで出力すれば、
Log4jで出力した内容をStringで取得できるのです。

適当にコピー&ペーストでお試しあれ。
…ちゃんと意味を理解したうえでね。

2012/07/08追記:微妙に修正

2 件のコメント:

  1. こんにちは。

    標準出力先の変更、なかなか使う機会がありませんが、覚えたてのころから知っているメソッドなので、よく注目しますね。

    返信削除
    返信
    1. 返信が遅れて失礼しました。
      コメントありがとうございます。

      覚えたてのことから知っているとはすごいです。
      確かに使う機会はそうそうありませんね。
      でも出来るということは知っておいて欲しい、
      そんな機能だと思っています。

      削除