[java] System.out.println ()에 대한 JUnit 테스트

제대로 설계되지 않은 표준 응용 프로그램에 많은 오류 메시지를 작성하는 이전 응용 프로그램에 대해 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의 원인입니다.


답변

나는 이것이 오래된 스레드라는 것을 알고 있지만 이것을 수행하는 멋진 라이브러리가 있습니다.

시스템 규칙

문서의 예 :

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사용하는 클래스를 리팩터링합니다 . 즉, 표준 출력 스트림을 직접 사용하지 않으려면 Dependency Injection을 사용하십시오.System.out.println()PrintStreamSystem.out

생산 중

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"));

토론

이런 식으로 테스트중인 클래스는 표준 출력을 간접적으로 리디렉션하거나 시스템 규칙을 방해하지 않고도 간단한 리팩토링을 통해 테스트 할 수 있습니다.


답변

setOut () 및 for in및을 통해 System.out 인쇄 스트림을 설정할 수 있습니다 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 메소드는 출력 스트림을 설정하고 해제하는 작업을 수행합니다. 의 메소드 구현 이 호출되면 테스트 블록에 대한 출력 생성에 액세스 할 수 있습니다.captureOutputCaptureTestCaptureOutputtest

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");
}