Programming

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

procodes 2020. 3. 4. 07:49
반응형

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

@dfa의 답변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()되면 다음과 같은 순서로 헬퍼 메소드를 호출합니다.

  1. setUpOutput()@Before주석으로 인해
  2. provideInput(String data)에서 호출 testCase1()
  3. getOutput()에서 호출 testCase1()
  4. restoreSystemInputOutput()@After주석으로 인해

필자가 System.err필요로하지 않았기 때문에 테스트 하지 않았지만 testing과 비슷하게 구현하기 쉬워야한다 System.out.


테스트 System.out전체 JUnit 5 예제 (언제 부분 교체) :

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 또는 로거 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);
}

참고 URL : https://stackoverflow.com/questions/1119385/junit-test-for-system-out-println



반응형