Backend/Java

[λ²ˆμ—­] Unit Testing of System.out.println() with JUnit

Seyun(Marco) 2023. 12. 30. 22:24
728x90

πŸ’‘ 원본 κΈ€ : https://www.baeldung.com/java-testing-system-out-println

1. μ†Œκ°œ

λ‹¨μœ„ ν…ŒμŠ€νŠΈλ₯Ό ν•  λ•Œ λ•Œλ•Œλ‘œ System.out.println()을 톡해 ν‘œμ€€ 좜λ ₯된 λ©”μ‹œμ§€λ₯Ό ν…ŒμŠ€νŠΈν•˜κ³  싢을 수 μžˆμŠ΅λ‹ˆλ‹€.

일반적으둜 ν‘œμ€€ 좜λ ₯보닀 λ‘œκΉ… ν”„λ ˆμž„μ›Œν¬λ₯Ό μ„ ν˜Έν•˜μ§€λ§Œ λ•Œλ•Œλ‘œ 이λ₯Ό μ‚¬μš©ν•  수 없을 μˆ˜λ„ μžˆμŠ΅λ‹ˆλ‹€.

이 λΉ λ₯Έ νŠœν† λ¦¬μ–Όμ—μ„œλŠ” JUnit을 μ‚¬μš©ν•˜μ—¬ System.out.println()을 λ‹¨μœ„ ν…ŒμŠ€νŠΈν•  수 μžˆλŠ” λͺ‡ 가지 방법을 μ‚΄νŽ΄λ³΄κ² μŠ΅λ‹ˆλ‹€.

2. κ°„λ‹¨ν•œ print λ©”μ„œλ“œ

이 νŠœν† λ¦¬μ–Όμ„ μ§„ν–‰ν•˜λŠ” λ™μ•ˆ μž‘μ„±ν•˜κ²Œ 될 ν…ŒμŠ€νŠΈλŠ” 주둜 ν‘œμ€€ 좜λ ₯ μŠ€νŠΈλ¦Όμ— κ²°κ³Όλ₯Ό 좜λ ₯ν•˜λŠ” κ°„λ‹¨ν•œ λ©”μ„œλ“œλ₯Ό λŒ€μƒμœΌλ‘œ 삼을 κ²ƒμž…λ‹ˆλ‹€:

private void print(String output) {
    System.out.println(output);
}

System.out λ³€μˆ˜λŠ” ν‘œμ€€ 좜λ ₯ μŠ€νŠΈλ¦Όμ„ λ‚˜νƒ€λ‚΄λŠ” public static final PrintStream 객체둜 μ‹œμŠ€ν…œ μ „λ°˜μ—μ„œ μ‚¬μš©λœλ‹€λŠ” 점을 λΉ λ₯΄κ²Œ μƒκΈ°μ‹œμΌœ λ“œλ¦½λ‹ˆλ‹€.

좔가적인 λ‚΄μš©


ν‘œμ€€ 좜λ ₯ 슀트림(standard output stream)μ΄λž€ 일반적으둜 μ½˜μ†” 화면에 데이터λ₯Ό 좜λ ₯ν•˜λŠ” 데 μ‚¬μš©λ˜λŠ” 슀트림으둜 μžλ°”μ—μ„œ System.out λ³€μˆ˜κ°€ ν‘œμ€€ 좜λ ₯ μŠ€νŠΈλ¦Όμ„ λ‚˜νƒ€λƒ…λ‹ˆλ‹€.

3. 핡심적인 Java λ™μž‘

μ΄μ œλΆ€ν„° println λ©”μ„œλ“œλ‘œ λ³΄λ‚΄λŠ” λ‚΄μš©μ„ ν™•μΈν•˜κΈ° μœ„ν•΄ λ‹¨μœ„ ν…ŒμŠ€νŠΈλ₯Ό μž‘μ„±ν•˜λŠ” 방법을 μ‚΄νŽ΄λ³΄κ² μŠ΅λ‹ˆλ‹€. κ·ΈλŸ¬λ‚˜, μ‹€μ œ λ‹¨μœ„ ν…ŒμŠ€νŠΈλ₯Ό μž‘μ„±ν•˜κΈ° 전에 ν…ŒμŠ€νŠΈμ—μ„œ 일뢀 μ΄ˆκΈ°ν™” μ½”λ“œλ₯Ό μ œκ³΅ν•΄μ£Όμ–΄μ•Ό ν•©λ‹ˆλ‹€.

private final PrintStream standardOut = System.out;
private final ByteArrayOutputStream outputStreamCaptor = new ByteArrayOutputStream();

@BeforeEach
public void setUp() {
    System.setOut(new PrintStream(outputStreamCaptor));
}

setUp λ©”μ„œλ“œμ—μ„œλŠ” ByteArrayOutputStream을 μ‚¬μš©ν•˜μ—¬ ν‘œμ€€ 좜λ ₯ μŠ€νŠΈλ¦Όμ„ μƒˆλ‘œμš΄ PrintStream에 μž¬ν• λ‹Ήν•©λ‹ˆλ‹€. μš°λ¦¬κ°€ 보게 될 좜λ ₯ μŠ€νŠΈλ¦Όμ€ μ΄μ œλΆ€ν„° 여기에 좜λ ₯될 κ²ƒμž…λ‹ˆλ‹€.

@Test
void givenSystemOutRedirection_whenInvokePrintln_thenOutputCaptorSuccess() {
    print("Hello Baeldung Readers!!");

    Assert.assertEquals("Hello Baeldung Readers!!", outputStreamCaptor.toString()
      .trim());
}

μš°λ¦¬κ°€ μž‘μ„±ν•œ ν…μŠ€νŠΈλ₯Ό print λ©”μ„œλ“œλ‘œ ν˜ΈμΆœν•œ ν›„, μš°λ¦¬κ°€ κΈ°λŒ€ν•œ μ½˜ν…μΈ κ°€ outputStreamCaptor에 ν¬ν•¨λ˜μ–΄ μžˆλŠ”μ§€ 확인해볼 수 μžˆμŠ΅λ‹ˆλ‹€. System.out.println()이 μΆ”κ°€ν•œ μƒˆ 쀄을 μ œκ±°ν•˜κΈ° μœ„ν•΄ trim()λ₯Ό ν˜ΈμΆœν•©λ‹ˆλ‹€.

ν‘œμ€€ 좜λ ₯ μŠ€νŠΈλ¦Όμ€ λ‹€λ₯Έ λΆ€λΆ„μ—μ„œ μ‚¬μš©λ˜λŠ” 곡유 정적 λ¦¬μ†ŒμŠ€μ΄λ―€λ‘œ ν…ŒμŠ€νŠΈκ°€ μ’…λ£Œλ˜λ©΄ μ›λž˜ μƒνƒœλ‘œ 볡원해야 ν•©λ‹ˆλ‹€.

@AfterEach
public void tearDown() {
    System.setOut(standardOut);
}

μ΄λ ‡κ²Œ ν•˜λ©΄ λ‚˜μ€‘μ— λ‹€λ₯Έ ν…ŒμŠ€νŠΈμ—μ„œ μ›μΉ˜ μ•ŠλŠ” λΆ€μž‘μš©μ΄ λ°œμƒν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.

System.setOutμ΄λž€?


ν•΄λ‹Ή λ©”μ„œλ“œλŠ” ν‘œμ€€ 좜λ ₯ μŠ€νŠΈλ¦Όμ„ μ œμ •μ˜ ν•˜λŠ”λ° μ‚¬μš©λ©λ‹ˆλ‹€.

public static void setOut(PrintStream out)

λ‹€λ₯Έ λΆ€λΆ„μ—μ„œ μ‚¬μš©λ˜λŠ” 곡유 정적 λ¦¬μ†ŒμŠ€λ‘œμ¨ ν…ŒμŠ€νŠΈκ°€ μ’…λ£Œλ˜λ©΄ μ›λž˜ μƒνƒœλ‘œ 볡원해야 ν•©λ‹ˆλ‹€.

ν•΄λ‹Ή λ©”μ„œλ“œλ₯Ό μ‚¬μš©ν•΄ μž¬μ •μ˜λ₯Ό ν•˜λ©΄ System.out둜 좜λ ₯ν•œ 데이터가 λ³€κ²½λ μˆ˜ μžˆμŠ΅λ‹ˆλ‹€.

4. μ‹œμŠ€ν…œ κ·œμΉ™ μ‚¬μš©

System 클래슀λ₯Ό μ‚¬μš©ν•˜λŠ” μ½”λ“œλ₯Ό ν…ŒμŠ€νŠΈ ν•˜κΈ° μœ„ν•œ Junit κ·œμΉ™μ„ μ œκ³΅ν•΄μ£ΌλŠ” System RulesλΌλŠ” μ™ΈλΆ€ 라이브러리λ₯Ό μ‚΄νŽ΄λ³΄κ² μŠ΅λ‹ˆλ‹€.

pom.xml에 μ•„λž˜μ™€ 같이 μ˜μ‘΄μ„±μ„ μΆ”κ°€ν•΄μ£Όλ©΄ λ©λ‹ˆλ‹€.

<dependency>
    <groupId>com.github.stefanbirkner</groupId>
    <artifactId>system-rules</artifactId>
    <version>1.19.0</version>
    <scope>test</scope>
</dependency>

gradleμ—μ„œλŠ” μ•„λž˜μ™€ 같이 μΆ”κ°€ν•˜λ©΄ λ©λ‹ˆλ‹€.

 

implementation("com.github.stefanbirkner:system-rules:1.19.0")


이제 λΌμ΄λΈŒλŸ¬λ¦¬κ°€ μ œκ³΅ν•˜λŠ” SystemOutRule 을 μ‚¬μš©ν•˜μ—¬ ν…ŒμŠ€νŠΈλ₯Ό μž‘μ„±ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

@Rule
public final SystemOutRule systemOutRule = new SystemOutRule().enableLog();

@Test
public void givenSystemOutRule_whenInvokePrintln_thenLogSuccess() {
    print("Hello Baeldung Readers!!");

    Assert.assertEquals("Hello Baeldung Readers!!", systemOutRule.getLog()
      .trim());
}

정말 λ©‹μ§‘λ‹ˆλ‹€! SystemOutRule 을 μ‚¬μš©ν•˜λ©΄ System.out 에 λŒ€ν•œ μ“°κΈ°λ₯Ό 차단할 수 μžˆμŠ΅λ‹ˆλ‹€.

λ¨Όμ € κ·œμΉ™μ—μ„œ enableLog() λ₯Ό ν˜ΈμΆœν•˜μ—¬ System.out에 기둝된 λͺ¨λ“  λ‚΄μš©μ„ κΈ°λ‘ν•˜κΈ° μ‹œμž‘ν•©λ‹ˆλ‹€.

그런 λ‹€μŒ enableLog()λ₯Ό ν˜ΈμΆœν–ˆκΈ° 떄문에 κ°„λ‹¨νžˆ getLog()λ₯Ό ν˜ΈμΆœν•˜μ—¬ System.out에 기둝된 ν…μŠ€νŠΈλ₯Ό κ°€μ Έμ˜΅λ‹ˆλ‹€.

이 κ·œμΉ™μ—λŠ” 항상 쀄 ꡬ뢄 κΈ°ν˜Έκ°€ \\\\n 인 둜그λ₯Ό λ°˜ν™˜ν•˜λŠ” λ©”μ„œλ“œλ„ ν¬ν•¨λ˜μ–΄ μžˆμŠ΅λ‹ˆλ‹€.

Assert.assertEquals("Hello Baeldung Readers!!\\n", systemOutRule.getLogWithNormalizedLineSeparator());

5. Junit5 및 Lambda와 ν•¨κ»˜ μ‹œμŠ€ν…œ κ·œμΉ™ μ‚¬μš©

Junit5μ—μ„œλŠ” κ·œμΉ™ λͺ¨λΈμ΄ extensions둜 λ³€κ²½λ˜μ—ˆμŠ΅λ‹ˆλ‹€. λ‹€ν–‰νžˆ, 4번 μ„Έμ…˜μ—μ„œ μ œμ‹œλœ μ‹œμŠ€ν…œ κ·œμΉ™ λΌμ΄λΈŒλŸ¬λ¦¬μ—μ„œλŠ” Junit5와 ν•¨κ»˜ λ™μž‘ν•˜λ„λ‘ μ€€λΉ„λœ λ‹€λ₯Έ λΌμ΄λΈŒλŸ¬λ¦¬κ°€ μžˆμŠ΅λ‹ˆλ‹€.

System LambdaλŠ” Maven Centralμ—μ„œ μ‚¬μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€. λ”°λΌμ„œ 이λ₯Ό pom.xml에 μΆ”κ°€ν•  수 μžˆμŠ΅λ‹ˆλ‹€:

<dependency>
    <groupId>com.github.stefanbirkner</groupId>
    <artifactId>system-lambda</artifactId>
    <version>1.2.1</version>
</dependency>

gradleμ—μ„œλŠ” μ•„λž˜μ™€ 같이 μΆ”κ°€ν•˜λ©΄ λ©λ‹ˆλ‹€.

implementation("com.github.stefanbirkner:system-lambda:1.2.1")

 

이제 이 라이브러리λ₯Ό μ‚¬μš©ν•˜μ—¬ ν…ŒμŠ€νŠΈλ₯Ό κ΅¬ν˜„ν•΄λ³΄κ² μŠ΅λ‹ˆλ‹€:

@Test
void givenTapSystemOut_whenInvokePrintln_thenOutputIsReturnedSuccessfully() throws Exception {

    String text = tapSystemOut(() -> {
        print("Hello Baeldung Readers!!");
    });

    Assert.assertEquals("Hello Baeldung Readers!!", text.trim());
}

이 λ²„μ „μ—μ„œλŠ” λͺ…령문을 μ‹€ν–‰ν•˜κ³  System.out에 μ „λ‹¬λœ μ½˜ν…μΈ λ₯Ό μΊ‘μ²˜ν•  수 μžˆλŠ” tapSystemOut λ©”μ„œλ“œλ₯Ό μ‚¬μš©ν•©λ‹ˆλ‹€.

6. κ²°λ‘ 

이 νŠœν† λ¦¬μ–Όμ—μ„œλŠ” System.out.printlnλ₯Ό ν…ŒμŠ€νŠΈν•˜κΈ° μœ„ν•œ λͺ‡ 가지 μ ‘κ·Ό 방식에 λŒ€ν•΄ λ°°μ› μŠ΅λ‹ˆλ‹€. 첫 번째 μ ‘κ·Ό λ°©μ‹μ—μ„œλŠ” 기본적인 Javaλ₯Ό μ‚¬μš©ν•˜μ—¬ ν‘œμ€€ 좜λ ₯ μŠ€νŠΈλ¦Όμ„ μž‘μ„±ν•˜λŠ” μœ„μΉ˜λ₯Ό λ¦¬λ””λ ‰μ…˜ν•˜λŠ” 방법을 μ‚΄νŽ΄λ³΄μ•˜μŠ΅λ‹ˆλ‹€.

그런 λ‹€μŒμ— Junit4 μŠ€νƒ€μΌ κ·œμΉ™μ„ μ‚¬μš©ν•˜κ³  λ‚˜μ€‘μ— λžŒλ‹€λ‘œ μž‘μ—…ν•˜λŠ” μ™ΈλΆ€ 라이브러리 μ‚¬μš© 방법을 λ°°μ› μŠ΅λ‹ˆλ‹€.

 

μ–Έμ œλ‚˜ 그렇듯이 κΈ°μ‚¬μ˜ 전체 μ†ŒμŠ€ μ½”λ“œλŠ” Githubμ—μ„œ 확인할 수 μžˆμŠ΅λ‹ˆλ‹€.

 

728x90
728x90