만족은 하되 안주하지는 말자

기록해야 기억한다

프로그래밍/JAVA

[Java] 단위테스트: System.out.println() 과 Scanner.in 이 들어간 코드를 어떻게 테스트할까?

D36choi 2021. 1. 19. 21:51
728x90

사용자 입출력 함수로 구성된 함수는 어떻게 테스트 할 수 있을까?

 

회사 개발자 교육 때 진행하는 페어프로그래밍에서, 최대한 강의를 통해 배운 단위 테스트를 해보려고 했지만

void 형식의 함수의 프린트 결과값을 통해 함수의 테스트결과를 알려고 할 때,  

그리고 입력 또한 함수 인자가 아닌 콘솔을 통한 입력을 받는 경우에 어떻게 테스트를 할 수 있을까에 대해 의문이 있었고

 

 

스택오버플로우의 힘을 통해 극복했다. 그 내용을 기록하고자 한다.

 

먼저 내가 테스트하려는 함수가 아래와 같다고 하자.

 

1. 사용자 입력을 받는다. 계산기 함수이므로 입력의 방식은 " {숫자} {연산자} {숫자} " 이어야만 한다.

2. 만약 위 형식 외에 다른 입력이 주어지는 경우엔 "올바른 계산식을 입력해주세요" 라는 메시지가 콘솔을 통해 프린트 된다.

3. 위 함수의 리턴값은 void 이며, 입력 또한 Scanner 를 이용한 사용자 입력 외엔 없다.

 

 

이 경우에는 일반적인 

Assertions.assertThat(calculator("1+2+3")).isEqualTo(6);

과 같은 Assertj를 활용한 테스트코드를 쓸 수 없을 것이다. 함수의 리턴값과 입력인자가 없기 때문이다.

 

그럴땐 아래와 같이 하면 된다.

public class CalculatorTest {	
    private static Calculator calculator;
    private static final String errMsg = "계산식을 입력해주세요.\n올바른 계산식을 입력해주세요.\n";
    @BeforeAll
    static void initAll() {
        calculator = new Calculator();
    }

@Test
    void 연산자_에러테스트() {

        String input = "1 + 2 k 3";
        OutputStream out = new ByteArrayOutputStream();
        System.setOut(new PrintStream(out));
        InputStream in = new ByteArrayInputStream(input.getBytes());
        System.setIn(in);
        calculator.start();
        Assertions.assertThat(errMsg).isEqualTo(out.toString());

    }
}

먼저 모든 테스트코드에 공통으로 활용할 객체나 메시지 값을 전역으로 선언한다.

 

"@BeforeAll" 어노테이션은 모든 테스트가 실행되기 전에 "1회" 실행되는 함수이다.

 

OutputStream out = new ByteArrayOutputStream();
System.setOut(new PrintStream(out));

 

을 통해, 콘솔에 출력되는 아웃풋을 바이트 아웃풋 스트림 out에 지정해 저장한다.

 

 

InputStream in = new ByteArrayInputStream(input.getBytes());
System.setIn(in);

 

을 통해 input 문자열을 시스템의 사용자입력으로 입력시킨다.

 

두 입출력을 지정한 뒤 계산기를 시작하면 콘솔에 입력할 필요없이 지정한 문자열이 Scanner 객체에 입력되게 된다.

 

그 후 Assertions.assertThat(errMsg).isEqualTo(out.toString()); 를 통해

 

에러 메시지와 아웃풋 텍스트의 문자열이 일치하는지를 통해 테스트 결과를 검증할 수 있다.

 

이것이 가능한 이유

java.lang.System.setOut() method reassigns the "standard" output stream.

System.setOut() 메서드는 사용자 표준 출력 스트림을 재지정할 수 있게 해준다.

콘솔 출력을 생성한 나의 아웃풋 스트림 객체에 지정함으로써 출력값을 해당객체에 저장할 수 있게 된다.

사용자 입력 또한 같다.

 

www.tutorialspoint.com/java/lang/system_setout.htm

 

Java.lang.System.setOut() Method - Tutorialspoint

Java.lang.System.setOut() Method Description The java.lang.System.setOut() method reassigns the "standard" output stream. Declaration Following is the declaration for java.lang.System.setOut() method public static void setOut(PrintStream out) Parameters ou

www.tutorialspoint.com

stackoverflow.com/questions/1119385/junit-test-for-system-out-println

 

JUnit test for System.out.println()

I need to write JUnit tests for an old application that's poorly designed and is writing a lot of error messages to standard output. When the getResponse(String request) method behaves correctly it

stackoverflow.com