JAVA/Junit

[Junit5] Mockito를 사용하는 방법

Yuco 2023. 10. 28. 15:12

1. Mockito의 제공

Mockito는 spring boot에서 사용하는 가장 기본적인 Mocking 프레임워크입니다.

spring boot의 maven이나 gradle에 spring-boot-starter-test가 있을 경우, mockito는  junit과 함께 자동적으로 제공됩니다. 

testImplementation 'org.springframework.boot:spring-boot-starter-test'

 

2. Mocking이 필요한 이유

test에는 기본적으로 단위 테스트, 통합 테스트가 있습니다. Mockito는 단위 테스트를 위해 사용됩니다. 단위 테스트의 목적은 단순히 해당 부분만 독립적으로 테스트 하기 위한 목적입니다. 그러므로 테스트 하려는 함수가 아닌 다른 함수들에 대해서까지 테스트를 진행하는 건 단위 테스트의 목표에 벗어납니다. 

 

단위 테스트 코드의 작성에 있어 mocking이 필요한 이유를 간단하게 설명하자면, 짜여진 모든 코드는 어떠한 흐름을 걸쳐 원하는 값을 return 하게 됩니다. 예를들어, 컨트롤러의 하나의 함수를 테스트 하기 위해서, 해당 컨트롤러에서 다른 서비스의 함수를 호출하고, 이로부터 원하는 값을 return 하는 방식은 spring 패턴의 가장 기본적인 방식입니다.

이렇듯 컨트롤러의 행위(단위)를 테스트함에 있어, 해당 컨트롤러가 아닌 다른 서비스나, 외부 서비스의 테스트 코드를 함께 작성하는 건 매우 비효율적인 방식이라고 볼 수 있습니다. 이러한 모든 코드의 행동을 테스트 하기 위해서는 단위 테스트가 아닌 통합 테스트가 필요합니다.

 

'컨트롤러 단위 테스트'란 단순히 해당 컨트롤러가 어떻게 행위 하는지에 대한 테스트를 위한 것이며, 해당 컨트롤러에서 호출하는 서비스나, 다른 외부 리소스를 함께 테스트 하는 건 아니기 때문입니다. 

 

그러므로 컨트롤러가 아닌 해당 컨트롤러에서 호출하는 다른 서비스의 행위를 가짜로 대체하며 원하는 값을 반환하기 위해 사용하는 게 Mockito며, 이는 함수를 Mocking 한다고 표현할 수 있습니다.

Mockito는 stub이란 기술을 사용하며, stub은 메소드의 반환 결과를 지정해놓을 수 있도록 도와줍니다. 

 

Mockito 에서는 아래와 같은 코드를 사용하여 컨트롤러에서 사용하는 서비스에 대한 retrun 값을 stub으로 설정해줄 수 있습니다.

 

3. Junit5 에서 Mockito 사용하기

 

3.1 @ExtendWith

@ExtendWith는 단위 테스트에 공통적으로 사용할 확장 기능을 선언해주는 역할을 합니다. Junit5 단위 테스트에서 Mockito를 사용하고 싶다면, 아래 코드와 같이 @ExtendWith의 인자로 MockitoExtension.class를 선언해주면 됩니다. 

 

3.2 @InjectMocks

@InjectMocks는 테스트하고자 하는 대상의 객체를 자동으로 주입시켜주는 역할을 합니다. 

@InjectMocks
private UserController userController;

위와 같이 UserController에 @InjectMocks 어노테이션을 사용할 경우, 아래와 같이 실제 테스트 대상에서 생성하고 있는 객체를 테스트 코드에 따로 작성해줄 필요가 없이 자동으로 주입됩니다.

private final UserService userService;
private final AuthService authService;

public UserController(
    UserService userService,
    AuthService authService
) {
    this.userService = userService;
    this.authService = authService;
}

하지만 @InjectMocks 를 통해 모든 생성자를 주입할 수 있는 건 아니며, 만약 @InjectMocks를 사용하지 못할 경우 실제 테스트 하고자 하는 테스트 코드에 생성자를 따로 생성해줘야 합니다.

 

3.3 @Mock

해당 테스트 대상이 아닌 해당 테스트에서 호출하고 있는 다른 서비스의 mocking을 위해 @Mock 어노테이션을 사용합니다. 이를 사용하게 되면 실제 서비스의 메서드는 가지고 있지만, 내부 구현은 없는 상태로 객체가 생성됩니다. 

@Mock
private UserService userService;

 

3.4 @Spy 

@Spy를 사용하게 되면 모든 기능을 가진 객체가 생성됩니다. @Spy 어노테이션을 쓸 경우 stub하지 않은 객체는 실제 메소드를 호출하며, stub된 객체는 반환 값을 설정할 수 있습니다. 이는 테스트 대상의 일부분만 Mocking할 수 있음을 의미합니다.

예를 들면, 실제 테스트 대상 서비스의 함수를 테스트 하려고 하는 경우, 테스트 하려고 하는 함수에 현재 테스트 대상 서비스의 다른 함수가 사용되는 경우 필요한 어노테이션입니다. 테스트 대상의 함수에서 해당 테스트 대상의 다른 함수를 호출하는 경우 유용합니다. 

위와 같은 경우 @InjectMocks 어노테이션과 @Spy 어노테이션을 함께 사용하는 게 일반적입니다.

 

대체로 @Mock을 쓰는 것을 추천하지만, 외부 라이브러리를 사용하는 테스트에서는 @Spy를 사용하는 게 필수적입니다. 

 

 

 

이는 실제 테스트 코드를 작성해보며 적어보는, 저의 경험이 담긴 글입니다. 혹시 틀린 부분이 있다면 댓글로 알려주세요 😀