設計が良くなく、標準出力にたくさんのエラーメッセージを書いている古いアプリケーション用のJUnitテストを書く必要があります。 getResponse(String request)
メソッドが正しく動作すると、XML応答が返されます。
@BeforeClass
public static void setUpClass() throws Exception {
Properties queries = loadPropertiesFile("requests.properties");
Properties responses = loadPropertiesFile("responses.properties");
instance = new ResponseGenerator(queries, responses);
}
@Test
public void testGetResponse() {
String request = "<some>request</some>";
String expResult = "<some>response</some>";
String result = instance.getResponse(request);
assertEquals(expResult, result);
}
しかし、XMLが不正な形式になったり、要求を理解できなかったりすると、null
が返され、標準出力に内容が書き込まれます。
JUnitでコンソール出力をアサートする方法はありますか?次のような場合を捉えるには
System.out.println("match found: " + strExpr);
System.out.println("xml not well formed: " + e.getMessage());
ByteArrayOutputStream とSystem.setXXXを使うのは簡単です:
private final ByteArrayOutputStream outContent = new ByteArrayOutputStream();
private final ByteArrayOutputStream errContent = new ByteArrayOutputStream();
private final PrintStream originalOut = System.out;
private final PrintStream originalErr = System.err;
@Before
public void setUpStreams() {
System.setOut(new PrintStream(outContent));
System.setErr(new PrintStream(errContent));
}
@After
public void restoreStreams() {
System.setOut(originalOut);
System.setErr(originalErr);
}
サンプルテストケース:
@Test
public void out() {
System.out.print("hello");
assertEquals("hello", outContent.toString());
}
@Test
public void err() {
System.err.print("hello again");
assertEquals("hello again", errContent.toString());
}
このコードを使用してコマンドラインオプションをテストしました(-versionがバージョン文字列などを出力することをアサートします)。
編集:この答えの以前のバージョンでは、テストの後にSystem.setOut(null)
と呼ばれていました。これは、コメント投稿者が言及しているNullPointerExceptionsの原因です。
これは古いスレッドですが、これを実行するためのNiceライブラリがあります。
ドキュメントからの例:
public void MyTest {
@Rule
public final SystemOutRule systemOutRule = new SystemOutRule().enableLog();
@Test
public void overrideProperty() {
System.out.print("hello world");
assertEquals("hello world", systemOutRule.getLog());
}
}
それはまたあなたがSystem.exit(-1)
やコマンドラインツールがテストされる必要があるであろう他のものをトラップすることを可能にするでしょう。
System.out
をリダイレクトする代わりに、共同作業者としてPrintStream
を渡し、テストでSystem.out
を使用し、テストでTest Spyを使用して、System.out.println()
を使用するクラスをリファクタリングします。つまり、標準出力ストリームを直接使用しないようにするには、Dependency Injectionを使用します。
製造中
ConsoleWriter writer = new ConsoleWriter(System.out));
テスト中
ByteArrayOutputStream outSpy = new ByteArrayOutputStream();
ConsoleWriter writer = new ConsoleWriter(new PrintStream(outSpy));
writer.printSomething();
assertThat(outSpy.toString(), is("expected output"));
ディスカッション
このように、テスト中のクラスは、標準出力の間接的なリダイレクトやシステムルールによる不明瞭な傍受を必要とせずに、単純なリファクタリングによってテスト可能になります。
System.out印刷ストリームは、 setOut() (およびin
およびerr
)を介して設定できます。これを文字列に記録する印刷ストリームにリダイレクトして、それを調べてもらえますか?それが最も単純なメカニズムのようです。
(私は、ある段階で、アプリを何らかのログ記録フレームワークに変換することを提唱します - しかし、私はあなたがすでにこれを知っていると思います!)
少し話題を外れていますが、SLF4Jでログ出力を取り込むことに興味を持っている人(私のように)がある場合は、 commons-testing のJUnit @Rule
が役に立ちます。
public class FooTest {
@Rule
public final ExpectedLogs logs = new ExpectedLogs() {{
captureFor(Foo.class, LogLevel.WARN);
}};
@Test
public void barShouldLogWarning() {
assertThat(logs.isEmpty(), is(true)); // Nothing captured yet.
// Logic using the class you are capturing logs for:
Foo foo = new Foo();
assertThat(foo.bar(), is(not(nullValue())));
// Assert content of the captured logs:
assertThat(logs.isEmpty(), is(false));
assertThat(logs.contains("Your warning message here"), is(true));
}
}
免責事項:
log4j
、log4j2
、およびlogback
のバインディングのみが使用可能ですが、もっと追加できれば幸いです。@dfaという回答は素晴らしいので、出力ブロックをテストできるようにするために、さらに一歩踏み出しました。
まず、厄介なクラスTestHelper
を受け入れるメソッドcaptureOutput
を使用してCaptureTest
を作成しました。 captureOutputメソッドは、出力ストリームを設定および破棄する作業を行います。 CaptureOutput
のtest
メソッドの実装が呼び出されると、テストブロック用の出力generateにアクセスできます。
TestHelperのソース:
public class TestHelper {
public static void captureOutput( CaptureTest test ) throws Exception {
ByteArrayOutputStream outContent = new ByteArrayOutputStream();
ByteArrayOutputStream errContent = new ByteArrayOutputStream();
System.setOut(new PrintStream(outContent));
System.setErr(new PrintStream(errContent));
test.test( outContent, errContent );
System.setOut(new PrintStream(new FileOutputStream(FileDescriptor.out)));
System.setErr(new PrintStream(new FileOutputStream(FileDescriptor.out)));
}
}
abstract class CaptureTest {
public abstract void test( ByteArrayOutputStream outContent, ByteArrayOutputStream errContent ) throws Exception;
}
TestHelperとCaptureTestは同じファイルに定義されています。
それからあなたのテストでは、あなたは静的なcaptureOutputをインポートすることができます。これはJUnitを使った例です:
// imports for junit
import static package.to.TestHelper.*;
public class SimpleTest {
@Test
public void testOutput() throws Exception {
captureOutput( new CaptureTest() {
@Override
public void test(ByteArrayOutputStream outContent, ByteArrayOutputStream errContent) throws Exception {
// code that writes to System.out
assertEquals( "the expected output\n", outContent.toString() );
}
});
}
もしあなたがSpring Bootを使っていたら(あなたは古いアプリケーションを使って作業していると言ったので、おそらくそうではないが他の人にとっては役に立つかもしれない)、そしてあなたはorg.springframework.boot.test)を使うことができる次のようにして、.rule.OutputCaptureとします。
@Rule
public OutputCapture outputCapture = new OutputCapture();
@Test
public void out() {
System.out.print("hello");
assertEquals(outputCapture.toString(), "hello");
}
@ dfa's answer および System.inのテスト方法を示す別の回答 に基づいて、プログラムに入力を与えてその出力をテストするための解決策を共有したいと思います。
参考として、JUnit 4.12を使います。
単純に入力を出力に複製するこのプログラムがあるとしましょう。
import Java.util.Scanner;
public class SimpleProgram {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.print(scanner.next());
scanner.close();
}
}
それをテストするために、以下のクラスを使うことができます。
import static org.junit.Assert.*;
import Java.io.*;
import org.junit.*;
public class SimpleProgramTest {
private final InputStream systemIn = System.in;
private final PrintStream systemOut = System.out;
private ByteArrayInputStream testIn;
private ByteArrayOutputStream testOut;
@Before
public void setUpOutput() {
testOut = new ByteArrayOutputStream();
System.setOut(new PrintStream(testOut));
}
private void provideInput(String data) {
testIn = new ByteArrayInputStream(data.getBytes());
System.setIn(testIn);
}
private String getOutput() {
return testOut.toString();
}
@After
public void restoreSystemInputOutput() {
System.setIn(systemIn);
System.setOut(systemOut);
}
@Test
public void testCase1() {
final String testString = "Hello!";
provideInput(testString);
SimpleProgram.main(new String[0]);
assertEquals(testString, getOutput());
}
}
私はそのコードが読解可能であると信じて私の情報源を引用したので、あまり説明しません。
JUnitがtestCase1()
を実行すると、ヘルパーメソッドは出現順に呼び出されます。
@Before
アノテーションが原因でsetUpOutput()
provideInput(String data)
から呼び出されるtestCase1()
getOutput()
から呼び出されるtestCase1()
@After
アノテーションが原因でrestoreSystemInputOutput()
System.err
は必要ないのでテストしませんでしたが、System.out
のテストと同様に、実装は簡単なはずです。
System.out
をテストするためのJUnit 5の完全な例(when部分を置き換えます):
package learning;
import static org.assertj.core.api.BDDAssertions.then;
import Java.io.ByteArrayOutputStream;
import Java.io.PrintStream;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
class SystemOutLT {
private PrintStream originalSystemOut;
private ByteArrayOutputStream systemOutContent;
@BeforeEach
void redirectSystemOutStream() {
originalSystemOut = System.out;
// given
systemOutContent = new ByteArrayOutputStream();
System.setOut(new PrintStream(systemOutContent));
}
@AfterEach
void restoreSystemOutStream() {
System.setOut(originalSystemOut);
}
@Test
void shouldPrintToSystemOut() {
// when
System.out.println("example");
then(systemOutContent.toString()).containsIgnoringCase("example");
}
}
System.outストリームをリダイレクトしたくないのは、それがENTIRE JVMにリダイレクトされるからです。 JVM上で実行されている他のものは混乱する可能性があります。入出力をテストするためのより良い方法があります。スタブ/モックを調べてください。
JUnitを使用中にsystem.out.printlnを使用したり、logger apiを使用して直接印刷することはできません。しかし、もしあなたが何か値をチェックしたいのなら、あなたは単に使うことができます
Assert.assertEquals("value", str);
それは以下のアサーションエラーを投げます:
Java.lang.AssertionError: expected [21.92] but found [value]
あなたの値は21.92であるべきです、今あなたがあなたのテストケースの下のようにこの値を使ってテストするならば合格します。
Assert.assertEquals(21.92, str);
外へ
@Test
void it_prints_out() {
PrintStream save_out=System.out;final ByteArrayOutputStream out = new ByteArrayOutputStream();System.setOut(new PrintStream(out));
System.out.println("Hello World!");
assertEquals("Hello World!\r\n", out.toString());
System.setOut(save_out);
}
誤りのため
@Test
void it_prints_err() {
PrintStream save_err=System.err;final ByteArrayOutputStream err= new ByteArrayOutputStream();System.setErr(new PrintStream(err));
System.err.println("Hello World!");
assertEquals("Hello World!\r\n", err.toString());
System.setErr(save_err);
}