본문 바로가기

ANDROID/Testing

[안드로이드] 람다 Function을 파라미터로 가지는 로직의 Unit Testing!

https://imgflip.com/i/1f3li7

자바에서 코틀린으로 넘어오면서 람다 식은 이제 부수적으로 쓴다기보단 항상 쓰게되는 부분 중에 하나가 되었다.

 

람다식을 단순히 이용할 때엔 간결하고 쉽게 사용이 가능하지만, 유닛 테스팅에 사용할 때, 특히 람다 Function을 파라미터로 포함한 function을 테스트해야 될 때는 이를 어떻게 Mock 해야 하는지, 가능은 한지에 대해서 머리를 싸매고 있었다.

 

결국에 개발이란 안 되는 건 없고, 방법을 찾지 못한 것이라는 말이 사실이었던 걸까, 방법은 있었다.

 

 mockito의 when 문을 이용해서 특정 function을 call 할때와, 이를 call 한 경우의 동작을 정의할 수 있는 것이었다.

 

방식은 아래와 같다.

 

우선 아래와 같은 클래스와 function을 예로 들어보겠다.

 

class sampleClass {

     fun refreshData(success () -> Unit, failed () -> Unit) {
    	itWorked()
    }
    
    // refreshData call시 function이 제대로 call 되었는지 테스트하기 위한 function
    fun itWorked() {}
}

 

위와 같은 refreshData를 유닛 테스팅하기 위해서는 refreshData를 Call 할 때 success와 failedd의 람다 코드블럭을 정의해주어야만 할 것이다.

 

다만 람다 코드블럭의 내용을 일일히 정의하고, 여기에 맞는 response를 확인토록 하는 건 테스팅 하는데도 불편하고, 테스팅을 위한 테스팅이 되는 느낌이 드는 것도 있다.

 

이를 when 문을 이용해 mocking해서, success가 된 경우를 아래와 같이 만들어 낼 수 있다.

 

아래의 테스트 코드는 refreshData call이 성공했을 경우를 테스트하기 위해 작성되었다.

@Mock
private lateinit var mockedSampleClass: SampleClass

// any returns null 오류 방지를 위한 별도의 function
private fun <T> any(): T {
    Mockito.any<T>()
    return null as T
}

@Before
fun `테스트 이전 세팅`() {
	MockitoAnnotations.initMocks(this)
}


@Test
fun `람다식을 파라미터로 포함하는 function 테스트`() {

	`when`(mockedSampleClass.refreshData(any(), any())
    	.thenAnswer{
        	it.getArgument<() -> Unit>(0).invoke()
        }
        
     mockedSampleClass.refreshData(
        success = {
        	println("yoo hoo success")
        }, 
        failed = {
        	println("yoo hoo failed")
        }
      )
        
     verify(mockedSampleClass, times(1)).itWorked()
}

 

중요한 지점은 any()를 이용해 어떠한 내용을 가지고 수행하느냐에 상관없이 success를 call하도록 하는 Mocking,

 

그리고 Mockito의 any()에서 오류가 발생하지 않도록 별도의 function을 지정하는 것,

 

마지막으로 람다식 mock 하는 부분의 thenReturn에서

it.getArgument<람다식 파라미터의 타입>(람다식 파라미터의 인덱스).invoke()

 

세가지를 체크하는 것이겠다.

 

여기서 람다식 파라미터의 인덱스란 해당 function에서 invoke하고자 하는 람다식 파라미터가 몇번째 파라미터로 위치하는지를 의미한다.

 

추가로 해당 람다식이 단순한 call을 요하는게 아니라 람다식 안에 또다른 파라미터가 들어간다면 이것도 역시

it.getArgument<람다식 파라미터의 타입>(람다식 파라미터의 인덱스).invoke(람다식이 필요로하는 파라미터 변수)

 

이런 형태로 구현이 가능하다!

 

< 참조 >

 

 

Test lambda functions in Android with Kotlin

I need to Unit-Test a function doClassAction and inside it calls the function doStoreAction on the object store. This doStoreAction accepts two parameters, one integer and one function, like in the

stackoverflow.com