programing

System.out.println()의 JUnit 테스트

goodsources 2022. 7. 30. 19:16
반응형

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

, 이 되거나 하지 못할 이 반환됩니다.null준준 、 출는는는는도 。

JUnit에서 콘솔 출력을 확인할 수 있는 방법이 있습니까?다음과 같은 사례를 포착하려면:

System.out.println("match found: " + strExpr);
System.out.println("xml not well formed: " + e.getMessage());

ByteArrayOutputStream 및 시스템을 사용합니다.setXX는 간단합니다.

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)이것이 Null 원인입니다.예외 코멘트란 다음을 말합니다.

오래된 스레드인 것은 알지만, 이것을 실현하기 위한 좋은 라이브러리가 있습니다.시스템 규칙
[ ] :

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는 , 을 사용하는 .System.out.println() PrintStream공동작업자로서, 그리고 그 다음에 사용하는System.out테스트 스파이를 테스트에 넣었습니다.즉, 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"));

논의

이와 같이 테스트 대상 클래스는 표준 출력을 간접적으로 리다이렉트하거나 시스템 규칙에 대한 불명확한 가로채기 없이 단순한 리팩터링으로 테스트할 수 있습니다.

시스템을 설정할 수 있습니다.setOut() 경유로 출력 스트림(및in ★★★★★★★★★★★★★★★★★」err이것을 문자열에 기록하는 인쇄 스트림으로 리다이렉트 한 후 검사할 수 있습니까?그것이 가장 간단한 메커니즘으로 보일 것이다.

(어느 단계에서 앱을 어떤 로깅 프레임워크로 변환하는 것을 추천하고 싶지만, 이미 알고 계실 겁니다!)

주제에서 약간 벗어났지만, (처음 이 스레드를 발견했을 때) 일부 사람들이 SLF4J를 통해 로그 출력을 캡처하는 데 관심이 있을 수 있습니다.공통 테스트의 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의 답변은 훌륭하기 때문에 한 걸음 더 나아가 out의 블록을 테스트할 수 있도록 했습니다.

처음 작성했습니다.TestHelper방법을 써서captureOutput귀찮은 수업을 받아들이다CaptureTestcaptureOutput 메서드는 출력 스트림을 설정 및 해체하는 작업을 수행합니다.의 실장 시기CaptureOutputtest메서드가 호출되며 테스트 블록의 출력 생성에 액세스할 수 있습니다.

Test Helper 소스:

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와 캡처는테스트는 같은 파일에 정의되어 있습니다.

그런 다음 테스트에서 static 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을 사용할 수 있습니다.다음과 같은 방법으로 Output Capture를 실행합니다.

@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필요 없지만 테스트와 마찬가지로 구현이 쉬워야 하기 때문입니다.System.out.

테스트할 JUnit 5의 완전한 예System.out(파트를 교환해 주세요.

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

시스템을 리다이렉트하지 않는 것이 좋습니다.JVM 전체로 리다이렉트되기 때문에 out stream을 클릭합니다.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);
}

기능이 시스템에 인쇄하는 경우.출력은 시스템을 사용하여 캡처할 수 있습니다.setOut 메서드를 사용하여 시스템을 변경합니다.사용자가 제공한 PrintStream으로 이동합니다.ByteArrayOutputStream에 연결된 PrintStream을 만들면 출력을 문자열로 캡처할 수 있습니다.

// Create a stream to hold the output
ByteArrayOutputStream baos = new ByteArrayOutputStream();
PrintStream ps = new PrintStream(baos);
// IMPORTANT: Save the old System.out!
PrintStream old = System.out;
// Tell Java to use your special stream
System.setOut(ps);
// Print some output: goes to your special stream
System.out.println("Foofoofoo!");
// Put things back
System.out.flush();
System.setOut(old);
// Show what happened
System.out.println("Here: " + baos.toString());

이 질문은 매우 오래되었고 이미 매우 좋은 답변을 가지고 있지만, 저는 대안을 제시하고자 합니다.는 이 답변이 마음에 들었습니다.dfa하지만 설정을 복사하지 않고 다른 프로젝트에서 재사용할 수 있는 무언가를 만들고 싶었기 때문에 이를 통해 라이브러리를 만들고 커뮤니티에 기여하고 싶었습니다.콘솔 캡터라고 불리며 다음 스니펫을 사용하여 추가할 수 있습니다.

<dependency>
    <groupId>io.github.hakky54</groupId>
    <artifactId>consolecaptor</artifactId>
    <version>1.0.0</version>
    <scope>test</scope>
</dependency>

클래스 예시

public class FooService {

    public void sayHello() {
        System.out.println("Keyboard not responding. Press any key to continue...");
        System.err.println("Congratulations, you are pregnant!");
    }

}

유닛 테스트

import static org.assertj.core.api.Assertions.assertThat;

import nl.altindag.console.ConsoleCaptor;
import org.junit.jupiter.api.Test;

public class FooServiceTest {

    @Test
    public void captureStandardAndErrorOutput() {
        ConsoleCaptor consoleCaptor = new ConsoleCaptor();

        FooService fooService = new FooService();
        fooService.sayHello();

        assertThat(consoleCaptor.getStandardOutput()).contains("Keyboard not responding. Press any key to continue...");
        assertThat(consoleCaptor.getErrorOutput()).contains("Congratulations, you are pregnant!");
        
        consoleCaptor.close();
    }
}

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

반응형