Test

테스트 더블

dev_roach 2021. 11. 19. 14:07
728x90

Effective Unit Test 에서 이 부분을 읽으면서 배운게 너무나도 많다.

그 내용을 한번 정리해보려고 한다.

테스트 더블이란?

테스트 더블이란 쉽게 예기하면 Stub 과 같이 특정 메소드가 완성되기 전에 대용해서 사용할수 있는 것을 뜻한다.

너무 추상적인거 같아서 쉽게 얘를 들면 Mocking 한 객체라고 생각해도 좋다.

순수히 그 메소드만 평가해야되는 단위 테스트에서는 이러한 테스트더블을 많이 사용하게 된다.

예를 들면 저번에 말했던 우리가 제어할 수 없는 대상을 테스트 더블로서 통제할 수 있는 것이다.

 

언제나 그렇듯, 코드로 보는것이 제일 나으니 코드로 한번 살펴보자.

예를 들어, 이런 Car class 가 있다고 해보자.

우리는 외부에서 Car 의 start 메소드 가 호출될때 Engine 의 start 메소드를 호출하는지 알고 싶다.

 

이럴경우 어떻게 테스트 해야할까? (기존 코드를 건드리면 안된다.)

방법이 없어 보이지 않는가?

Mockito 를 사용했었던 사람들은 verify 를 생각할지도 모른다.

하지만 기본 자바코드로는 어떻게 구현할까? 우리는 자바 프로그래머지 Mockito 프로그래머가 아니다.

어떻게 Car 안에서 start() 메소드가 호출된다는 사실을 알 수 있을까?

이럴 경우 사용할 수 있는 것이 바로 테스트 더블이다.

아래 코드를 한번 보자.

우리는 TestEngine 이라는 테스트 더블을 생성했다.

테스트 더블을 사용함으로서 얻을 수 있는 결과는 엔진메소드가 호출됬는지 여부다.

즉, 우리가 애초에 테스트 해야할 경우도 메소드의 호출 여부이므로 테스트 더블을 통해 완벽하게 수행해냈다.

 

우리가 위의 코드를 보면서 또 어떤 것을 느낄 수 있을까?

바로 객체안의 숨겨진 정보 또한 꺼내올 수 있다는 것이다.

즉, 우리는 객체안의 숨겨진 engine.start() 의 실행여부 정보에 대해서 취득할 수 있게 됬다.

우리는 감춰진 정보를 테스트 더블을 통해서 얻어낸 것이다.

 

두번째로 얻어가야 할점은 이게 가능한 이유는 바로 DI 구조를 지녔기 때문이다. 

즉 쉽게 얘기하면 Car 의 Engine Dependency 는 외부에서 결정한다.

우리가 쉬운 테스트 구조 혹은 좋은 아키텍쳐 구조를 위해서라도 DI 를 사용해야 하는 이유 중 하나이다.

다시한번 명심해야 할건 우린 자바 프로그래머지, 프레임워크 프로그래머가 아니다.

 

여하튼 테스트 더블은 이와 같이 사용할 수 있다.

그렇다면 테스트 더블은 또 어떤 이점이 있을까?

테스트 대상 코드를 격리한다.

이건 테스트를 공부해보거나, 사용해봤다면 어느정도 알고 있는 사람이 많을거라고 생각한다.

한가지 예시를 들자면,

결제 시스템에서 외부에서 결제가 성공했을때 DB 에 결제 히스토리가 잘 저장되는지를 테스트 해본다고 해보자.

 

외부에서 결제가 성공/실패 여부는 우리가 통제 불가능한 대상이다.

예를 들어 결제 요청을 보냈는데 외부 결제 서버가 죽었다고 해보자. 

이를 통제 가능한가? 

이처럼 외부요인은 통제 불능의 대상이다.

그렇다면 어떻게 해야할까? 바로 이럴때 테스트 더블을 이용하는 것이다.

한번 예시코드를 작성해보자.

일단 RoachPaymentService 의 isPayed() 라는 메소드를 생성해보자.

isPayed() 는 결제 성공여부를 boolean 값으로 리턴한다.

만약 RoachPaymentService 는 죽은 상태라 실제로는 계속 결제가 실패하는 상황이라고 해보자.

그렇다고 해서 당신의 테스트 코드가 실패해야 되는가..?

그건 아니다. 테스트는 외부 요인으로 부터 독립적이여야 한다. 

 

내 코드를 한번 작성해보자.

테스트 설명을 위한 글이니.. 코드는 감안하고 봐주면 될거 같다.

일단 대략적으로 외부 결제가 성공해야지만 DB 에 저장한다. 라고 생각하면 된다.

그렇담 우리가 일단 결제가 성공했을때 DB 에 저장하는 save 메소드를 호출하는지 테스트 한다고 해보자.

그럼 어떻게 테스트 더블을 해야할까?

위와 같이 테스트 더블을 하면 외부 결제는 반드시 성공한다는 조건으로 테스트가 가능할 것 이다.

또한 DataBase 의 Test 메소드를 호출하는 것 또한 알 수 있다.

 

잠깐, 얘기를 하나하자면 반드시는 아니지만 코드를 따라쳐보는게 더 잘 와닿을 것이다.

어떻게 짜는 것이 테스트에 좋은 구조인지도 감이 올 것이다.

 

이렇게 테스트 더블을 이용하면 아래와 같은 장점을 얻었다는 것을 느낄 수 있다.

 

1. 테스트 속도가 빠르다 -> 외부 요인은 네트워킹 대상이고 응답을 기다려야만 한다.

2. 외부요인에 기대지 않고, 오직 내가 짠 로직만을 테스트 가능하다. 

3. 특수한 상황을 시뮬레이션 한다 -> 외부 결제에서 성공했을시 저장한다는 것. 

 

느낀점

테스트 더블에 대해서 사실 잘 모르고 있었다.

그리고 어떻게 프레임워크가 이걸 구성하고 있는지도 생각해본적이 잘 없다.

요걸 알고나니깐 사실 JUnit 과 같은 테스팅 프레임워크를 만들어보고 싶다는 생각이 든다.

728x90