Junit 과 Mockito 기반의 Spring 단위 테스트 코드 작성법
참고 링크
1. Mockito 소개 및 사용법
Mockito 란?
Mockito란 Java 오픈소스 테스트 프레임워크입니다. Mockito를 사용하면 실제 객체를 모방한 가짜 객체, Mock 객체 생성이 가능해집니다. 개발자는 이 Mock 객체를 통해 테스트를 보다 간단하고 통일성있게 구현할 수 있습니다.
일반적으로 Spring 으로 웹 애플리케이션을 개발하면, 여러 객체들 간의 의존성이 생긴다. 이러한 의존성은 단위 테스트 작성을 어렵게 하는데, 이를 해결하기 위해서는 가짜 객체를 주입시켜주는 Mockito 라이브러리를 사용할 수 있다. Mockito 를 활용하면 가짜 객체에 원하는 결과를 Stub 하여 단위 테스트를 진행할 수 있다. 물론 프레임워크 도구가 필요없다면 사용하지 않는 것이 가장 좋다.
Mockito 사용법
1. Mock 객체 의존성 주입
Mockito 에서 가짜 객체의 의존성 주입을 위해서 크게 3가지 어노테이션이 사용된다.
@Mock: 가짜 객체를 만들어 반환해주는 어노테이션@Spy: Stub 하지 않은 메서드들은 원본 메서드 그대로 사용하는 어노테이션@InjectMocks:@Mock또는@Spy로 생성된 가짜 객체를 자동으로 주입시켜주는 어노테이션
예를 들어 UserController 에 대한 단위 테스트를 작성하고자 할 때, UserService 를 사용하고 있다면, @Mock 어노테이션을 통해 가짜 UserService 를 만들고 @InjectMocks 를 통해 UserController 에 이를 주입시킬 수 있다.
2. Stub 로 결과 처리
앞서 설명하였듯, 의존성이 있는 객체는 가짜 객체를 주입하여 어떤 결과를 반환하라고 정해진 답변을 준비시켜야 한다. Mockito 에서는 다음과 같은 stub 메서드를 제공한다.
doReturn(): 가짜 객체가 특정한 값을 반환해야 하는 경우doNothing(): 가짜 객체가 아무 것도 반환하지 않는 경우doThrow(): 가짜 객체가 예외를 발생시키는 경우
3. Mockito 와 Junit 의 결합
Mockito 도 테스팅 프레임워크이기 때문에 Junit 과 결합되기 위해서는 별도의 작업이 필요하다.
기존의 JUnit4 에서 Mockito 를 활용하기 위해서는 클래스 어노테이션으로 @RunWith(MockitoJuintRunner.class) 를 붙여주어야 연동이 가능했다. 하지만 SpringBoot 2.2.0 부터 공식적으로 Junit5 를 지원함에 따라 이제부터는 @ExtendWith(MockitoExtenstion.class) 를 사용해야 결합이 가능하다.
2. Spring 컨트롤러 단위 테스트 작성 예시
사용자 회원가입, 목록 조회 API
단위 테스트 작성 준비
앞서 설명하였듯 Junit5 와 Mockito를 연동하기 위해서는 @ExtendWith(MockitoExtension.class) 를 사용해야 한다. 이를 클래스 어노테이션으로 붙여 다음과 같은 테스트 클래스를 작성할 수 없다.
이제 의존성 주입을 해주어야 한다. 먼저 테스트 대상인 UserController 에는 가짜 객체 주입을 위한 @InjectMocks 를 붙여주어야 한다. 그리고 UserService 에는 가짜 객체 생성을 위해 @Mock 어노테이션을 붙여주면 된다.
컨트롤러를 테스트하기 위해서는 HTTP 호출이 필요하다. 일반적인 방법으로는 HTTP 호출이 불가능하므로 스프링에서는 이를 위한 MockMVC 를 제공하고 있다. MockMVC 는 다음과 같이 생성할 수 있다.
그러면 이제 UserController 를 테스트하기 위한 준비가 끝났으므로, 다음의 케이스들에 대해서 테스트 코드를 작성해주도록 하자.
회원가입 성공
사용자 목록 조회
1. 회원가입 성공 테스트
우성 회원가입 요청을 보내기 위해서는 SignUpRequest 객체 1개와 UserService 의 signUp 에 대한 Stub 이 필요하다. 이러한 준비 작업을 해주면 given 단계에 다음과 같은 테스트 코드가 작성된다.
HTTP 요청을 보내면 Spring 내부에서 MessageConverter 를 사용해 Json String 을 객체로 반환한다. 그런데 이것은 Spring 내부에서 진행되므로, 우리가 API 로 전달되는 파라미터인 SignUpRequest 를 조작할 수 없다.
그래서 SignUpRequest 클래스 타입이라면 어떠한 객체도 처리할 수 있도록 any() 가 적용되었다. any() 를 사용할 때에는 특정 클래스의 타입을 지정해주는 것이 좋다.
그 다음 when 단계에서는 mockMVC 에 데이터와 함께 POST 요청을 보내야 한다. 요청 정보는 mockMvc 의 perform 에서 작성 가능한데, 요청 정보에는 MockMvcRequestBuilders 가 사용되며 요청 메소드 종류, 내용, 파라미터 등을 설정할 수 있다.
보내는 데이터는 객체가 아닌 문자열이어야 하므로 별도의 변환이 필요하므로 Gson 을 사용해 변환하였다.
마지막으로 호출된 결과를 검증하는 then 단계에서는 회원가입 API 호출 결과로 200 Response 와 응답 결과를 검증해야 한다. 응답 검증 시에는 jsonPath 를 이용해 해당 json 값이 존재하는지 확인하면 된다.
2. 사용자 목록 조회 테스트
사용자 목록 조회 given 단계에서는 UserService findAll 에 대한 Stub 이 필요하다.
그리고 when 단계에서는 호출하는 HTTP 메서드를 GET 으로 , URL 을 "/users/list" 로 작성해주어야 한다. 마지막으로 then 단계에서는 HTTP Status 가 OK 이며, 주어진 데이터가 올바른지를 검증해야 하는데, 이번에는 Json 응답을 객체로 변환하려 확인하자.
@WebMvcTest
위와 같이 MockMvc 를 생성하는 등의 작업은 번거롭다. 다행히도 SpringBoot 는 컨트롤러 테스트를 위한 @WebMvcTest 어노테이션을 제공하고 있다. 이를 이용하면 MockMvc 객체가 자동으로 생성될 뿐 아니라, ControllerAdvice 나, Filter, Interceptor 등 웹 계층 테스트에 필요한 요소들을 모두 빈으로 등록해 스프링 컨텍스트 환경을 구성한다. @WebMvcTest 는 스프링부트가 제공하는 테스트 환경이므로 @Mock 과 @Spy 대신 각각 @MockBean 과 @SpyBean 을 사용해주어야 한다.
주의할 점
스프링은 내부적으로 스프링 컨텍스트를 캐싱해두고 동일한 테스트 환경이라면 재사용한다. 그런데 특정 컨트롤러만을 빈으로 만들고 @MockBran 과 @SpyBean 으로 빈을 모킹하는 @WebMvcTest 는 캐싱의 효과를 거의 얻지 못하고 새로운 컨텍스트의 생성을 필요로 한다.
그러므로 빠른 테스트를 원한다면 직접 MockMvc 를 생성했던 처음의 방법을 사용하는 것이 좋을 수 있다.
3. Spring 서비스 계층 단위 테스트 작성 예시
사용자 회원가입/목록 조회 비지니스 로직
사용자 회원가입과 목록 조회를 위해서는 다음과 같은 비지니스 로직 레이어(Service Layer) 가 필요하다.
단위 테스트 작성 준비
앞서 설명하였듯 @ExtendWith(MockitoExtension.class) 와 가짜 객체 주입을 사용해 다음과 같은 테스트 클래스를 작성할 수 있다.
이번에는 BCryptPasswordEncoder 에 @Spy 를 사용하였다. 앞서 설명하였듯 Spy 는 Stub 하지 않은 메서드는 실제 메서드로 동작하게 하는데, 위의 예제에서 실제로 사용자 비밀번호를 암호화해야하므로, @Spy 를 사용하였다. 이번에도 테스트 코드를 작성해보자.
회원가입 성공
사용자 목록 조회
1. 회원가입 성공 테스트
이번에는 추가적으로 mockito 의 verify() 를 사용해보았다. verity 는 Mockito 라이브러리를 통해 만들어진 가짜 객체의 특정 메서드가 호출된 횟수를 검증할 수 있다.
위에서는 passwordEncoder 의 encode 메서드와 userRepository 의 save 메서드가 각각 1번만 호출되었는지를 검증하기 위해서 사용했다.
2. 사용자 목록 조회 테스트
4. Spring 레포지토리 계층 단위 테스트 작성 예시
사용자 추가/목록 조회 코드
사용자 회원가입과 목록 조회를 위한 JPA 레포지토리 인터페이스는 다음과 같이 구현되어 있다.
이번에도 역시 다음과 같은 기능들에 대한 테스트 코드를 작성해보도록 하자
사용자 추가
사용자 목록 조회
@DataJpaTest 어노테이션
스프링 부트는 JPA 레포지토리를 손쉽게 테스트 할 수 있는 @DataJpaTest 어노테이션을 제공하고 있다.
@DataJpaTest 를 사용하면 기본적으로 인메모리 데이터베이스인 H2 를 기반으로 테스트용 테이터베이스를 구축하며, 테스트가 끝나면 트랜잭션을 롤백 해준다. 레포지토리 계층은 실제 DB 와 통신없이 단순 모킹하는 것은 의미가 없으므로 직접 데이터베이스와 통신하는 @DataJpaTest 를 사용하도록 하자.
1. 사용자 추가 테스트
2. 사용자 목록 테스트
Last updated