Help us understand the problem. What is going on with this article?

Java コンソール出力(標準出力・標準エラー出力)内容をString文字列として取り出す

More than 3 years have passed since last update.

標準出力・標準エラー出力の出力先をコンソールからオブジェクト内に変更する。
コンソール出力内容をテストしたい場合に使用する。

ConsoleSnatcher.java
package testlib.system;

import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;

/**
 * 標準出力・標準エラー出力の出力先をコンソールから変更するクラス.くbr>
 * コンソール出力内容をテストしたい場合に使用する。<br>
 * 本クラスはスレッドセーフではないのでマルチスレッド環境では利用不可。くbr>
 * シングルトンクラス。<br>
 * log4jの出力内容を取得する場合はConsoleAppenderで、標準出力する。
 *
 * @author boss_ape
 */
public final class ConsoleSnatcher
{
    /** シングルトンパターンのためのインスタンス. */
    private static final ConsoleSnatcher INSTANCE = new ConsoleSnatcher();

    /** 変更前の標準出力. */
    private PrintStream nativeOut = null;

    /** 元の標準エラー出力. */
    private PrintStream nativeErr = null;

    /** 変更後の標準出力. */
    private ByteArrayOutputStream snatchedOut = new ByteArrayOutputStream();

    /** 変更後の標準エラー出力. */
    private ByteArrayOutputStream snatchedErr = new ByteArrayOutputStream();

    /** 出力先変更済みフラグ. */
    private boolean stealFlag = false;

    /**
     * デフォルトコンストラクタの禁止.
     */
    private ConsoleSnatcher() { }

    /**
     * このクラスのインスタンスを取得
     *
     * @return このクラスのインスタンス
     */
    public static ConsoleSnatcher getlnstance()
    {
        return INSTANCE;
    }

    /**
     * 出力先をコンソールからこのクラスへ出力するように変更する.
     */
    public void snatch()
    {
        // 2回以上の変更禁止
        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;
    }

    /**
     * 出力先を元に戻すくbr>
     * 使用後は必ずこのメソッドを呼び出す。
     */
    public void release()
    {
        // 出力先が変更されている場合のみ実施
        if (stealFlag) {
            clearOutput();
            clearErrorOutput();

            //出力先を元に戻す
            System.setOut(nativeOut);
            System.setErr(nativeErr);

            stealFlag = false;
        }
    }
}

以下、使用サンプル。
まずはコンソール出力するJavaサンプル

SampleConsoleOutput.java
package jp.co.ivynetwork.batch;

/**
 * 標準出力と標準エラー出力するサンプル処理.<br>
 * 0:正常終了<br>
 * 1:異常終了
 *
 * @author boss_ape
 */
public class SampleConsoleOutput
{
    /**
     * fugafugaをhogehogeする
     *
     * @param args パラメータnullではない事
     */
    public static void hogeHoge(String arg)
    {
        try {
            // パラメータがnullならException発生
            if (null == arg)
                throw new NullPointerException();

        } catch (NullPointerException e) {
            // 標準エラー出力
            System.err.print("エラーが発生");
            return;
        }

        // 標準出力
        System.out.print("正常終了");
        return;
    }
}

上記クラスのJUnitテスト

SampleConsoleOutputTest.java
package jp.co.ivynetwork.batch;

import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;

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

import testlib.system.ConsoleSnatcher;

/**
 * SampleConsoleOutputクラステスト.<br>
 *
 * @author boss_ape
 */
public class SampleConsoleOutputTest
{
    /** 標準出力・標準エラー出力変更管理オブジェクト. */
    private ConsoleSnatcher stdout = ConsoleSnatcher.getlnstance();

    /**
     * テストメソッド実行前にSystem.exit()設定を変更.
     */
    @Before
    public void changeSystem()
    {
        // 標準出力・標準エラー出力先を変更
        stdout.snatch();
    }

    /**
     * 標準出力・標準エラー出力先設定を戻す.<br>
     * バッファにたまっていた内容を最後にコンソール出力する。
     */
    @After
    public void resetSystem()
    {
        // バッファにたまっている内容を一時退避
        String std = stdout.getOutput();
        String std_err = stdout.getErrorOutput();

        // コンソール出力に戻す
        stdout.release();

        // コンソール出力
        if (std.length() > 0) {
            System.out.println(System.getProperty("line.separator") + "--------");
            System.out.println(std);
        }
        if (std_err.length() > 0) {
            System.out.println(System.getProperty("line.separator") + "--------");
            System.out.println(std_err);
        }
    }

    /**
     * hogeHogeメソッド正常系のテスト.
     */
    @Test
    public void testHogeHoge正しいパラメータを渡す()
    {
        // バッチ実行
        SampleConsoleOutput.hogeHoge("正しいパラメータ");

        String std = stdout.getOutput();
        String std_err = stdout.getErrorOutput();

        assertThat(std_err.length(), is(0));
        assertThat(std, is("正常終了"));
    }

    /**
     * hogeHogeメソッド異常系のテスト.
     */
    @Test
    public void testHogeHogeパラメータnullを渡す()
    {
        // hogeHoge実行
        SampleConsoleOutput.hogeHoge(null);

        String std = stdout.getOutput();
        String std_err = stdout.getErrorOutput();

        assertThat(std_err, is("エラーが発生"));
        assertThat(std.length(), is(0));
    }
}
boss_ape
IT技術者 ソフトウェア構成管理(DevOps)、アプリケーションアーキテクトの技術を追うのが好き 趣味:鉛筆画、レザークラフト、本自炊、旅行
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした