우테코

[우테코 8기 프리코스] 2주차 NsTest 공부하기

bang-dev 2025. 10. 23. 21:58


NsTest. 처음봤다.

사실 테스트 코드를 적은지도 얼마 되지 않았지만,

테스트 하는  AssertJ나 JUnit5를 공부하려는 참에 우테코에서 제공하는 NsTest가 무엇인지 먼저 알고 들어가야 할 것 같아서 먼저 정리하려고 한다.

 

1. 코드 

우테코에서 제공하는 라이브러리

 

우테코에서 제공하는 라이브러리 들이다. 이 중에서 이 포스트에는 NsTest만 정리 할 예정이다.

 

 

제공되는 ApplicaitionTest 코드를 확인하면 NeTest를 상속받고 있다. 

NeTest를 첫 주에는 그냥 넘어갔는데 이번 주는 확인해보자.

 

NsTest

public abstract class NsTest {
    private PrintStream standardOut;
    private OutputStream captor;

    @BeforeEach
    protected final void init() {
        standardOut = System.out;
        captor = new ByteArrayOutputStream();
        System.setOut(new PrintStream(captor));
    }

    @AfterEach
    protected final void printOutput() {
        System.setOut(standardOut);
        System.out.println(output());
    }

    protected final String output() {
        return captor.toString().trim();
    }

    protected final void run(final String... args) {
        try {
            command(args);
            runMain();
        } finally {
            Console.close();
        }
    }

    protected final void runException(final String... args) {
        try {
            run(args);
        } catch (final NoSuchElementException ignore) {
        }
    }

    private void command(final String... args) {
        final byte[] buf = String.join("\n", args).getBytes();
        System.setIn(new ByteArrayInputStream(buf));
    }

    protected abstract void runMain();
}

전체코드는 이렇다. 위에서 부터 들어가보자.

 

private PrintStream standardOut;
private OutputStream captor;

@BeforeEach
protected final void init() {
    standardOut = System.out;
    captor = new ByteArrayOutputStream();
    System.setOut(new PrintStream(captor));
}

@AfterEach
protected final void printOutput() {
    System.setOut(standardOut);
    System.out.println(output());
}

protected final String output() {
    return captor.toString().trim();
}

 

@BeforeEach와 @AfterEach는 Junit5의 어노테이션이다. Junit5는 아래 훌륭한 글이 있어서 이 글에서 확인해주세용.

https://donghyeon.dev/junit/2021/04/11/JUnit5-%EC%99%84%EB%B2%BD-%EA%B0%80%EC%9D%B4%EB%93%9C/

 

JUnit5 완벽 가이드

시작하기전

donghyeon.dev

 

먼저 멤버 변수 부터 알아보도록 하자.

 

PrintStream

데이터를 바이트(byte) 스트림에 편리하게 "출력"할 수 있도록 도와주는 포장지 같은 클래스.

 

init() 메서드

protected final void init() {
		//콘솔 출력을 standardOut에 저장 (테스트가 끝난 후 복구하기 위해)
        standardOut = System.out;
        
        //ByteArrayOutputStream는 바이트 데이터를 메모리에 저장하는 통로
        captor = new ByteArrayOutputStream();
        
        //System.out의 목적지를 captor로 변경
        //ex.System.out.println("안녕") 입력 -> captor 변수에 "안녕" 이 바이트 형태로 쌓임.
        System.setOut(new PrintStream(captor));
    }

 

왜 할까?

콘솔에 출력되는 내용을 자동으로 검증하기 위해서이다!

 

ApplicaitionTest

 

NsTest

 

run() 메서드를 확인해보면 command(args) 가 보일 것이다. 이는

private void command(final String... args) {
    final byte[] buf = String.join("\n", args).getBytes();
    System.setIn(new ByteArrayInputStream(buf));
}

 

이렇게 돼있는데, 예를 들어 설명하면 

"pobi,woni", "1"

-> "pobi,woni\n1" 로 줄바꿈 해서 인식

 

이후 바이트 배열로 변환한 다음 

System.setIn(new ByteArrayInputStream(buf));

이것을 통해 사용자가 직접 입력한 것과 같은 환경을 만듭니다.

 

이제 어떻게 실행되는지 흐름을 이야기 하면

 

  • MyTest 객체의 run() 메서드를 찾음
  • MyTest에는 run()이 없으니, 부모인 NsTest의 run() 메서드를 실행
  • NsTest의 run() 메서드가 1번 command(args)를 실행
  • NsTest의 run() 메서드가 2번 runMain()을 호출
  • 이때! 자바는 runMain()이 자식 클래스(MyTest)에 @Override (재정의) 된 것을 확인하고, 부모의 빈칸이 아닌 자식이 채워 넣은 runMain()을 실행
  • 결과적으로 Application.main(new String[]{})이 실행
  • NsTest의 run() 메서드가 3번 finally의 Console.close()를 실행

이런 흐름으로 테스트가 이루어 진다.

 

이제 마지막 

@AfterEach
protected final void printOutput() {
    System.setOut(standardOut);
    System.out.println(output());
}

protected final String output() {
    return captor.toString().trim();
}

 

을 보면, output()은 captor의 바이트 들을 공백을 제거한 String 형식으로 반환하고, 

마지막 printOutput() 메서드의  System.setOut(standardOut);로 출력 위치를 변환시킨 후

System.out.println(output());를 통해 captor의 내용들을 콘솔로 출력하게 한다.

 

줄이며..

NsTest를 깊게 뜯지도 않고 얕게 뜯어보았는데 이것만 봐도 다형성이 얼마나 흥미로운지 확인 할 수 있었다!!

다음에는 JUnit5도 정리해서 올려보로록 하겠다!!

만관부~~