본문 바로가기

Android/이론

[안드로이드] Koin으로 주입된 viewModel 쉽게 Unit 테스트 하기

728x90
반응형

 

 

  • dependency 추가

기본으로 추가되어있는 dependency 외에 mockito, truth를 추가하였습니다.

dependencies {

    implementation 'androidx.core:core-ktx:1.3.2'
    implementation 'androidx.appcompat:appcompat:1.2.0'
    implementation 'com.google.android.material:material:1.3.0'
    implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
    
    testImplementation 'junit:junit:4.13.2'
    androidTestImplementation 'androidx.test.ext:junit:1.1.2'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'

    // test
    androidTestImplementation("org.mockito:mockito-android:2.24.5")
    testImplementation 'org.mockito:mockito-inline:2.21.0'
    testImplementation "com.google.truth:truth:1.0"
 }

 

  • Repository, ViewModel

test할 Repository 와 ViewModel은 다음과 같습니다.

class Repository {

    // TEST
    val deviceEmg1 = BleDevice(EMG_DEVICE_1, MutableLiveData(false), MutableLiveData(false),null)
    val deviceEmg2 = BleDevice(EMG_DEVICE_2, MutableLiveData(false), MutableLiveData(false),null)
    val deviceLight1 = BleDevice(LIGHT_DEVICE_1, MutableLiveData(false),null,null)
    val deviceLight2 = BleDevice(LIGHT_DEVICE_2, MutableLiveData(false),null,null)


    fun getEmg1Connected() = deviceEmg1.connected
    fun getEmg2Connected() = deviceEmg2.connected
    fun getEmg1Calibrated() = deviceEmg1.calibrated
    fun getEmg2Calibrated() = deviceEmg2.calibrated
 }

 

class MeasureViewModel(private val repository: Repository): ViewModel() {

    private val emg1Connected: LiveData<Boolean>
        get() = repository.getEmg1Connected()
    private val emg2Connected: LiveData<Boolean>
        get() = repository.getEmg2Connected()
    private val emg1Calibrated: LiveData<Boolean>?
        get() = repository.getEmg1Calibrated()
    private val emg2Calibrated: LiveData<Boolean>?
        get() = repository.getEmg2Calibrated()

    fun reqCalibration(): Boolean{
        var req1 = true
        var req2 = true
        if(emg1Connected.value == true){
            if(emg1Calibrated?.value == true) req1 = false
        }else{
            req1 = false
        }
        if(emg2Connected.value == true){
            if(emg2Calibrated?.value == true) req2 = false
        }else{
            req2 = false
        }
        return (req1 or req2)
    }


}

repository의 LiveData에 영향을 받는 변수를 가지는 메서드 reqCalibration()가 test 대상입니다.

메서드가 잘 작동하는지 확인하기 위해, repository의 값을 바꾸며 test할 수 있습니다.

 

  • KoinTest

Koin 사용을 위해 AbstractKoinTest class를 만들고, 상속하여 사용합니다.

abstract class AbstractKoinTest: KoinTest {
    @get:Rule
    val koinTestRule = KoinTestRule.create{
        printLogger(Level.DEBUG)
        modules(listOf(repositoryModule, viewModelModule, fragmentModule)) //myModule
    }

    @get:Rule
    val mockProvider = MockProviderRule.create{
        clazz-> Mockito.mock(clazz.java)
    }
}

 

  • ExampleUnitTest

unitTest이므로 androidTest가 아닌 test폴더에서 하여줍니다.

import androidx.lifecycle.MutableLiveData
import com.google.common.truth.Truth.assertThat
import com.robotnmore.rmfit.viewmodel.MeasureViewModel
import org.junit.Test
import org.junit.Before
import org.koin.test.inject
import org.koin.test.mock.declareMock
import org.mockito.BDDMockito.given
import org.mockito.Mockito.`when`

class ExampleUnitTest: AbstractKoinTest() {


    val measureViewModel: MeasureViewModel by inject()

    @Before
    fun setup(){

    }

    @Test
    fun `EMG 디바이스의 연결, Calibration 상태에 따른 Calibration 요구`(){


        // Given
        val repository = declareMock<Repository>{
            given(getEmg1Connected()).willReturn(MutableLiveData(true))
            given(getEmg2Connected()).willReturn(MutableLiveData(true))
            given(getEmg1Calibrated()).willReturn(MutableLiveData(true))
            given(getEmg2Calibrated()).willReturn(MutableLiveData(true))
        }

        //`when`(repository.getEmg1Calibrated()).thenReturn(MutableLiveData(false))


        // When
        val result = measureViewModel.reqCalibration()


        // Then
        assertThat(result).isFalse()
    }
}

위와 같이 간단히 테스트할 수 있습니다.

AbstractKoinTest()를 상속하고,

간단히 viewModel을 주입하여 줍니다.

@Before을 사용하여 Test전에 setup()메서드를 먼저 실행할 수 있습니다.

  • @Test

Given-When-Then 패턴으로 나누었습니다.

Given : declareMock과 given을 이용하여 repository를 가짜로 생성해주고, 함수들의 리턴 값을 설정해줍니다.

또는 `when`을 이용하여 설정할 수 있습니다.

When :  test할 메서드인 reqCalibration()를 실행합니다. 

Then : 결과가 예상과 맞는지 확인합니다.

 

그다음 해당 class를 run 해주면, 예상과 맞는지 결과를 확인할 수 있습니다. 

 

Test를 통과했다고 하네요 ^^

 

 

참고 자료 

728x90
반응형