ArgumentCaptor 란?
ArgumentCaptor란 interaction을 기록하는 Mock 타입의 Test Double을 만드는 객체이다. 즉, ArgumentCaptor은 객체의 interaction을 기록한다.
ArgumentCaptor 사용하기 위한 환경 설정
ArgumentCaptor을 사용하기 위해서 앞선 글 https://simcode.tistory.com/12 의 환경을 가져와서 LoginUseCase, LoginUseCaseResult, LoginRepository, LoginRepositoryResult를 사용한다. 환경 설정 부분을 읽도록 하자.
ArgumentCaptor 사용한 테스트 만들기
LoginUseCase의 logIn메서드에서 userName과 password변형이 생겨서 LoginRepository에 같은 userName과 password가 넘어가지 않았다면 문제가 생기는 상황이라 가정하자. 이런 상황에서는 LoginUseCase에서 특정한 userName과 password로 로그인을 시도했을 때 해당 파라미터가 LoginRepository로 잘 넘어갔는지 확인하는 테스트를 만들어야 한다.
변수 정의하기
먼저 userName과 password라는 변수를 만들고 token과 repositorySuccessResult를 미리 정의한다.
val userName = "test"
val password = "password"
val token = "test_token"
val repositorySuccessResult = LoginRepositoryResult.Success(token)
Stub 만들기
위에서 정의한 userName과 password로 로그인을 시도했을 때 respositorySuccessResult를 return하는 Stub을 생성한다.
*Stub: 특정한 요청에 미리 정의된 객체를 반환하는 Test Double
Mockito.`when`(loginRepository.login(userName = userName, password = password))
.thenReturn(repositorySuccessResult)
LoginUseCase에 로그인 요청하기
이제 LoginUseCase에 로그인을 요청하면 된다.
val result = loginUseCase.logIn(userName = userName, password = password)
ArgumentCaptor로 LoginRepository의 interaction 기록하기
LoginUseCase는 logIn요청 시 loginRepository에 logIn 요청을 넘긴다. 이때 useName, password 파라미터가 잘 넘어갔는지 ArgumentCaptor을 통해 기록해보자.
class LoginUseCase(
private val loginRepository: LoginRepository
) {
fun logIn(userName: String, password: String): LoginUseCaseResult {
return when (val result = loginRepository.login(userName, password)) {
is LoginRepositoryResult.Success -> LoginUseCaseResult.Success(result.token)
is LoginRepositoryResult.Fail -> LoginUseCaseResult.Success(result.error)
}
}
}
argumentCaptor을 선언하고 argumentCaptor<String>()을 통해 초기화 한 후 loginRepository의 logIn메서드가 1번 수행되고 해당 logIn 시 넘어간 userName과 password를 capture()메서드로 argumentCaptor에 기록 한다.
val argumentCaptor = argumentCaptor<String>()
Mockito.verify(loginRepository, times(1)).login(argumentCaptor.capture(), argumentCaptor.capture())
ArgumentCaptor검증하기
ArgumentCaptor은 allValues 호출 시 capture()한 모든 값을 순서대로 List로 반환한다.
val allValues: List<T>
get() = captor.allValues
따라서 다음과 같이 사용하면 userName과 password가 제대로 입력되었는지 검증할 수 있다.
assertEquals(userName, argumentCaptor.allValues[0])
assertEquals(password, argumentCaptor.allValues[1])
assertEquals(LoginUseCaseResult.Success(token), result)
전체 코드
전체 코드는 다음과 같다. 앞 setUp()메서드는 이전 글에서 다룬 것과 같아서 이 글에서는 환경 설정에서 생략했다.
package lecture4
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Test
import org.mockito.Mockito
import org.mockito.Mockito.times
import org.mockito.kotlin.argumentCaptor
class LoginUseCaseArgumentCaptorTest {
private lateinit var loginUseCase: LoginUseCase
private lateinit var loginRepository: LoginRepository
@Before
fun setUp() {
loginRepository = Mockito.mock(LoginRepository::class.java)
loginUseCase = LoginUseCase(loginRepository)
}
@Test
fun testLoginSuccessMockArgumentCaptor() {
val userName = "test"
val password = "password"
val token = "test_token"
val repositorySuccessResult = LoginRepositoryResult.Success(token)
Mockito.`when`(loginRepository.login(userName = userName, password = password))
.thenReturn(repositorySuccessResult)
val result = loginUseCase.logIn(userName = userName, password = password)
val argumentCaptor = argumentCaptor<String>()
Mockito.verify(loginRepository, times(1)).login(argumentCaptor.capture(), argumentCaptor.capture())
assertEquals(userName, argumentCaptor.allValues[0])
assertEquals(password, argumentCaptor.allValues[1])
assertEquals(LoginUseCaseResult.Success(token), result)
}
}
정리
Test Double은 Test를 위해 필요한 3가지 유형 Mock, Stub, Fake을 가진다. ArgumentCaptor는 Mock 유형으로 객체의 Interaction을 기록하기 위한 객체이다. ArgumentCaptor을 사용하면 손쉽게 Interaction을 기록하고 테스트 할 수 있다.
*만약 Test Double에 대해 잘 모른다면, https://simcode.tistory.com/10 를 참고하자.