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