티스토리 뷰

반응형

 

mockito는 스스로를 자바의 단위 테스트를 위한 '맛있는' 프레임워크라고 부르고 있다.

mockito를 사용하면 기존 코드, 혹은 아직 만들지 않은 코드에 대한 스텁(Stub)을 만들 수 있다.

mockito와 Stub을 통한 테스트가 어떤 장점이 있길래 많은 사람들이 이를 사용하는 것일까?

 

 


Out-In 개발은 비즈니스 요구 사항이 명확하지 않은 상태에서 빠르게 사용자 인터페이스부터 설계하는 방식이다. 사용자의 진입점을 먼저 정의한 뒤 비즈니스 로직들에 대한 설계가 이루어진다. 내부 구현에 대한 설계가 완벽하게 이루어지지 않아 Out-In 방식으로 사용자의 진입점부터 개발하는 상황을 생각해 보자.

 

간단하게 하나의 서비스 클래스를 의존하는 컨트롤러를 아래와 같이 만들었다.

 

 

MockitoService라는 서비스 클래스를 사용, 입력된 정수에 값을 더하거나 뺀 결과를 문자열로 반환하는 두 개의 메서드를 사용하려고 하나 아직 서비스 클래스의 껍데기만 생성된 상태다. 나는 사용자가 "/mockito"로 접속하면 두 메서드의 결과인 "30"과 "10"을 합친 "3010"을 받기를 원한다.

 

 

테스트는 중요하다. 테스트를 통해서 방금 작성한 코드가 의도대로 움직이는지 검증해보고 싶다. mockito() 메서드에 대한 단위 테스트는 어떻게 구현할 수 있을까?

 

JUnit5만을 사용하여 테스트를 아래와 같이 작성할 수 있다.

 

 

테스트를 금방 작성하고 이것이 통과하는지 확인하고 싶으나 아직 서비스 클래스의 "add10()", "minus10()" 메서드를 구현하지 않았으므로 실행해 볼 수 없다.

 

 

Mock이 없다면 유저 인터페이스만 테스트를 하기는 어렵다.

 

"add10()"과 "minus10()"을 구현한 뒤 테스트를 실행시키면 정상적으로 통과할 것은 자명하다.

그러나 서비스의 비즈니스 요구 사항이 매우 복잡하다면 MockitoService 클래스를 쉽게 구현할 수 있을까?

만약 서비스의 처리 결과를 여러 DB에 복잡하게 저장해야 한다면?

 

Out-In 방식의 개발을 하기로 했는데, 가장 Out에 위치한 유저의 인터페이스를 만들고 테스트하려고 했더니 관련된 모든 비즈니스 로직들을 구현해야 하는 상황에 놓였다.

만약 내부의 구현이 가능하다고 해도 구현과 테스트와의 호흡이 너무 길어진다. 열심히 구현 후 테스트를 실행했더니 실패했다면 유저의 인터페이스부터 다단계의 비즈니스 로직 중 어느 곳에서 문제가 발생했는지 하나하나 찾아보아야 한다.

 

Out-In 방식의 개발을 하면 단위 테스트를 만드는 것은 불가능할까? 가장 작은 단위의 비즈니스 로직부터 구현하는 In-Out 방식만이 옳은 것인지 의심이 든다.

 

 

하나만 테스트하는지 모호하다.

 

구현을 마치고 위의 테스트가 정상적으로 작동한다고 해도 컨트롤러의 mockito() 메서드만을 테스트하는 것인지는 논란의 여지가 있다. 위와 같은 Classical Testing을 살펴보면 컨트롤러를 테스트하는 도중 서비스의 두 메서드 add10(), reduce10()을 사용한다.

mockito() 메서드의 결과가 "3010"인 건 서비스의 두 메서드 결과인 "3010"과 빈 문자열 ""이 합쳐졌기 때문일 수도 있다. 이것은 우리가 원하는 동작은 아니며 이 테스트는 컨트롤러 메서드만을 테스트하는 것인지도 모호하다.

 

 

BDD Mockito를 통한 Stubbing

 

Mockito 프레임워크를 사용하면 위의 두 문제가 해결된다.

서비스 클래스의 내부 구현은 아직 고민하지 않은 상태이므로 아래와 같이 메서드의 껍데기만 만들어 놓는다.

 

 

아래와 같이 간단히 BDD Mockito를 통해 기존의 테스트를 재구성하면 서비스를 구현하지 않은 상태에서도 컨트롤러에 대한 단위 테스트를 수행할 수 있다.

 

 

Mock 객체인 서비스 클래스를 컨트롤러가 사용하도록 주입(@InjectMocks) 받게 만들고, 서비스 클래스의 두 메서드가 입력에 따라 어떤 값을 반환할지 먼저 정의한다(Stubbing).

 

 

테스트 수행 시 사용되는 mockitoService의 객체는 우리가 정의한 것이 아닌 가짜 객체가 대신 사용된다.

 

서비스의 구현 없이도 컨트롤러 테스트가 가능하다

 

이로써 Out-In 방식으로 내부의 비즈니스 로직을 구현하지 않아도 유저 인터페이스부터 작성하고 테스팅을 할 수 있게 되었으며,

컨트롤러가 의존하는 서비스의 메서드는 입력에 대해 지정된 값만을 내놓는 가짜이므로, 컨트롤러의 mockito() 메서드만을 테스트하는 것이 명확해졌다.

 

 


 

마무리

 

mockito를 통해 객체 간 의존을 고민하지 않고 작은 단위 테스트를 수행할 수 있으며, TDD와 같이 테스트와 구현 사이클을 짧게 가져가는 개발 방식에서는 더더욱 유용하게 쓰인다.

 

하지만 위의 에시에서 서비스 클래스의 변경이 일어나도 컨트롤러에서는 그것을 Stubbing 하므로 테스트가 정상적으로 통과된다. 의존하는 객체에 문제가 생겼는데 테스트가 통과한다는 점은 어찌 보면 단점일 수도 있다.

따라서 인수 테스트를 별도로 작성해 통해 진짜 객체들을 사용하여도 문제가 없는지를 보장하여 이것을 보완하자.

반응형