Android
[Android] VewModel LiveData Unit Test
Xmobile
2020. 10. 12. 11:07
ViewModel 에서 api통신후 LiveData를 참조하여 정상적으로 갱신되었는지 확인하여
정상 동작하는지 테스트를 진행해 보도록 하겠습니다.
LiveData확인을 위한 확장함수를 활용하여 진행 하도록 합니다.
build.gradle
// Testing dependencies
androidTestImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:1.3.5"
kaptAndroidTest "com.google.dagger:hilt-android-compiler:2.28.3-alpha"
androidTestImplementation "androidx.arch.core:core-testing:2.0.0"
// Espresso dependencies
androidTestImplementation "androidx.test.espresso:espresso-contrib:3.1.1"
androidTestImplementation "androidx.test.espresso:espresso-core:3.1.1"
androidTestImplementation "androidx.test.espresso:espresso-intents:3.1.1"
// Assertions
androidTestImplementation "androidx.test.ext:junit:1.1.0"
androidTestImplementation "com.google.truth:truth:0.42"
androidTestImplementation "androidx.test.uiautomator:uiautomator:2.2.0"
androidTestImplementation "androidx.work:work-testing:2.1.0"
androidTestImplementation "com.google.dagger:hilt-android-testing:2.28.3-alpha"
testImplementation "junit:junit:4.12"
MainRepository
class MainRepository(private val apiService: ApiService) {
suspend fun getLotto(order: Int) = apiService.getLotto(order)
}
ApiService
interface ApiService {
@GET("api/lotto")
suspend fun getLotto(@Query("order") order: Int ): HomeDto
companion object {
private const val BASE_URL = "http://192.168.0.103:8181/"
fun create(): ApiService {
val logger = HttpLoggingInterceptor().apply { level = HttpLoggingInterceptor.Level.BODY }
val client = OkHttpClient.Builder()
.addInterceptor(logger)
.build()
return Retrofit.Builder()
.baseUrl(BASE_URL)
.client(client)
.addConverterFactory(GsonConverterFactory.create())
.build()
.create(ApiService::class.java)
}
}
}
LiveDataTestUtil
@VisibleForTesting(otherwise = VisibleForTesting.NONE)
fun <T> LiveData<T>.getOrAwaitValue(
time: Long = 2,
timeUnit: TimeUnit = TimeUnit.SECONDS,
afterObserve: () -> Unit = {}
): T {
var data: T? = null
val latch = CountDownLatch(1)
val observer = object : Observer<T> {
override fun onChanged(o: T?) {
data = o
latch.countDown()
this@getOrAwaitValue.removeObserver(this)
}
}
this.observeForever(observer)
try {
afterObserve.invoke()
// Don't wait indefinitely if the LiveData is not set.
if (!latch.await(time, timeUnit)) {
throw TimeoutException("LiveData value was never set.")
}
} finally {
this.removeObserver(observer)
}
@Suppress("UNCHECKED_CAST")
return data as T
}
HomeViewModel
getLotto메소드는 retrofit을 사용하여 서버 api를 호출 후 homeDto 라이브 데이터를 갱신합니다.
class HomeViewModel @ViewModelInject constructor(val mainRepository: MainRepository
) : ViewModel() {
var isLoading = MutableLiveData<Boolean>()
var homeDto = MutableLiveData<HomeDto>()
fun getLotto(order: Int) {
try {
isLoading.postValue(true)
viewModelScope.launch {
homeDto.postValue(mainRepository.getLotto(order))
}
} catch (e: Exception) {
homeDto.postValue(null)
} finally {
isLoading.postValue(false)
}
}
}
HomeViewModelTest
Api 호출후 livedata를 확인하도록 합니다.
@HiltAndroidTest
@ExperimentalCoroutinesApi
class HomeViewModelTest {
private lateinit var viewModel: HomeViewModel
private val hiltRule = HiltAndroidRule(this)
private val instantTaskExecutorRule = InstantTaskExecutorRule()
@get:Rule
val rule = RuleChain
.outerRule(hiltRule)
.around(instantTaskExecutorRule)
@Inject
lateinit var mainRepository: MainRepository
@Before
fun setUp() {
hiltRule.inject()
val context = InstrumentationRegistry.getInstrumentation().targetContext
viewModel = HomeViewModel(mainRepository)
}
@Test
fun callRequest() {
//호출 후 livedata를 확인
viewModel.getLotto(0)
assertTrue(viewModel.homeDto.getOrAwaitValue()._order > 0 )
}
}
정상적으로 테스트 확인.
이를 바탕으로 viewmodel livedata을 검증하는 방법에 대해서 확인해 보았습니다.