Image
Testing Codes/MockK

[MockK] every를 match와 같이 사용해 입력값을 범위로 지정하기

입력값을 특정 값으로 지정하는 것의 한계

입력값을 다음과 같이 지정하게 되면, getNameByUserId에 0xffffffff이 입력되었을 때만 "TestUser"이 반환된다. 

every { userRepository.getNameByUserId("0xffffffff") } returns "TestUser"

그렇다면 만약 유효한 아이디 일 경우에 "TestUser"을 반환하고, 유효하지 않은 아이디일 경우에는 예외를 반환하는 목 객체는 어떻게 만들 수 있을까? 만약 유효한 아이디가 8자리의 16진수값이라면, 이에 대한 모든 값에 대해 every를 작성하게 되면 수십억줄의 코드를 작성해야 할 수 있다.

이런 경우 만약 입력 값을 특정 범위로 지정할 수 있다면 매우 도움이 될 것이다. 이를 위해 MockK라이브러리는 입력 값을 범위로 지정할 수 있는 방법을 지원한다. 이어서 이 방법에 대해 알아보자.

 

every를 match와 같이 사용해 입력값을 범위로 지정하기

입력값에 match를 사용하면 특정한 범위의 값들을 입력값으로 지정할 수 있다. 예를 들어 문자가 0x로 시작하고, 총 길이가 10이며, 3번째 자리부터 10번째 자리까지 16진수인 경우의 반환값을 "TestUser"로 설정하기 위해서는 다음과 같이 작성될 수 있다.

every {
    userRepository.getNameByUserId(
        match {
            it.startsWith("0x") &&
            it.length == 10 &&
            isHexadecimal(it.substring(2))
        }
    )
}.returns("TestUser")

*물론 아이디가 유효한지 판단하는 것은 객체가 해야 하지만, 여기서는 예시를 위해 이렇게 만들었다.

 

이런 방식으로 아이디가 유효할 때만 유저의 이름을 반환하도록 만들고 이외의 경우에는 IllegalArgumentException을 발생하도록 만들면, 유효한 아이디를 사용해 유저 이름이 조회되었을 때 UserRepository가 제대로 결과를 반환하도록 만들 수 있다. 

class UserProfileFetcherTest {
    @Test
    fun dummyTest() {
        // Given
        val userRepository: UserRepository = mockk()
        val userProfileFetcher = UserProfileFetcher(
            userRepository = userRepository
        )
        every {
            userRepository.getNameByUserId(
                match {
                    it.startsWith("0x") &&
                    it.length == 10 &&
                    isHexadecimal(it.substring(2))
                }
            )
        }.returns("TestUser")
        every {
            userRepository.getNameByUserId(
                match {
                    (
                            it.startsWith("0x") &&
                            it.length == 10 &&
                            isHexadecimal(it.substring(2))
                    ).not()
                }
            )
        }.throws(IllegalArgumentException("UserIdError"))

        // When
        val result = userProfileFetcher.getUserProfileById("0x12345678")

        // Then
        assertEquals("TestUser", result.name)
    }

    private fun isHexadecimal(input: String): Boolean {
        val hexRegex = Regex("^[0-9A-Fa-f]+$")
        return hexRegex.matches(input)
    }
}

테스트를 실행해보면 테스트가 성공하는 것을 확인할 수 있다.

 

그림1. 테스트 성공

 

그렇다면 유효하지 않은 아이디로 같은 테스트를 진행했을 때 성공하는지 살펴보자.

class UserProfileFetcherTest {
    @Test
    fun dummyTest() {
        // Given
        val userRepository: UserRepository = mockk()
        val userProfileFetcher = UserProfileFetcher(
            userRepository = userRepository
        )
        every {
            userRepository.getNameByUserId(
                match {
                    it.startsWith("0x") &&
                    it.length == 10 &&
                    isHexadecimal(it.substring(2))
                }
            )
        }.returns("TestUser")
        every {
            userRepository.getNameByUserId(
                match {
                    (
                            it.startsWith("0x") &&
                            it.length == 10 &&
                            isHexadecimal(it.substring(2))
                    ).not()
                }
            )
        }.throws(IllegalArgumentException("UserIdError"))

        // When
        val result = userProfileFetcher.getUserProfileById("0xzzzzzzzz") // 유효하지 않은 아이디

        // Then
        assertEquals("TestUser", result.name)
    }

    private fun isHexadecimal(input: String): Boolean {
        val hexRegex = Regex("^[0-9A-Fa-f]+$")
        return hexRegex.matches(input)
    }
}

그러면 IllegalArgumentException이 발생하며 테스트가 실패하는 것을 볼 수 있다.

 

그림2. 테스트 실패

 

정리

every와 match를 함께 사용하면 입력 값을 범위로 설정할 수 있다. 만약 특정한 범위의 입력에 대해서만 특정한 결과값이 나와야 한다면 match를 사용하도록 하자.

반응형

 

이 글의 저작권은 '조세영의 Kotlin World' 에 있습니다. 글, 이미지 무단 재배포 및 변경을 금지합니다.

 

 

Kotlin, Android, Spring 사용자 오픈 카톡

오셔서 궁금한 점을 질문해보세요!
비밀번호 : kotlin22

open.kakao.com