协程单元测试kotlinx.coroutines.CoroutineContextKt.newCoroutineContext上的Mockk java.lang.AbstractMethodError

时间:2019-04-24 15:09:48

标签: android unit-testing kotlin kotlin-coroutines mockk

我想对视图模态中的方法进行单元测试,但是每次失败时,我都要遍历许多网站并收集答案,但没有一个能帮上忙。 我只想在我的视图模式中测试一个方法loadWeatherForeCast

我浏览了以下链接, Error calling Dispatchers.setMain() in unit test

https://android.jlelse.eu/mastering-coroutines-android-unit-tests-8bc0d082bf15

https://proandroiddev.com/mocking-coroutines-7024073a8c09

import androidx.arch.core.executor.testing.InstantTaskExecutorRule
import io.mockk.MockKAnnotations
import io.mockk.every
import io.mockk.impl.annotations.MockK
import io.mockk.junit5.MockKExtension
import io.mockk.verify
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.InternalCoroutinesApi
import kotlinx.coroutines.runBlocking
import org.junit.Rule
import org.junit.jupiter.api.BeforeAll
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.TestInstance
import go_jek.domain.entities.LocationTemperature
import go_jek.domain.interactor.Result
import go_jek.domain.interactor.weatherUseCase.WeatherParam
import go_jek.domain.interactor.weatherUseCase.WeatherUseCase
import go_jek.domain.repository.ApiDataSource
import go_jek.utility.dateUtils.DateUtils

@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class WeatherViewModelTest {
@get:Rule
val rule = InstantTaskExecutorRule()

@MockK
lateinit var apiDataSource: ApiDataSource       //Interface

@MockK
lateinit var dateUtilImpl: DateUtils                //Interface

@MockK
lateinit var weatherParam: WeatherParam

@MockK
lateinit var locationTemperatureOutput: LocationTemperature

private lateinit var weatherUseCase: WeatherUseCase
private lateinit var weatherViewModel: WeatherViewModel

@BeforeAll
fun setup() {
    MockKAnnotations.init(this)

    weatherUseCase = WeatherUseCase(apiDataSource)
    weatherViewModel = WeatherViewModel(weatherUseCase, dateUtilImpl, Dispatchers.Unconfined)
}

@Test
fun check() = runBlocking {
    every {
        weatherUseCase.execute(any(), any(), any())
    } answers {
        thirdArg<(Result<LocationTemperature>) -> Unit>().invoke(Result.Success(locationTemperatureOutput))
    }

    weatherViewModel.loadWeatherForeCast(32.45, 72.452)

    verify(atLeast = 1) {
        apiDataSource.getWeatherInfo(weatherParam)
    }
}
}


interface ApiDataSource {
fun getWeatherInfo(weatherParam: WeatherParam):   Result<LocationTemperature>
}


 class WeatherUseCase(var apiDataSource: ApiDataSource) :   UseCase<LocationTemperature, WeatherParam>() {
override suspend fun run(params: WeatherParam): Result<LocationTemperature> = apiDataSource.getWeatherInfo(params)

}

 class WeatherParam(
val apiKey: String = BuildConfig.appixu_secretkey,
val location: String
  ) : UseCase.NoParams()


 class LocationTemperature(val location: Location, val current: Current, val forecast: Forecast) : Parcelable


 class WeatherViewModel (val weatherUseCase: WeatherUseCase,
                    val dateUtilImpl: DateUtils,
                    val uiContext: CoroutineContext = Dispatchers.Main) : ViewModel(), CoroutineScope {

private val job: Job

private val _locationLiveData = MutableLiveData<LocationTemperature>()
val locationLiveData: LiveData<LocationTemperature>

private val _error: MutableLiveData<String> = MutableLiveData()
var error: LiveData<String> = _error

private val loadingState = MutableLiveData<Boolean>()
val loadingLiveData = loadingState

override val coroutineContext: CoroutineContext
    get() = uiContext + job

init {
    job = Job()

    locationLiveData = Transformations.map(_locationLiveData) {
        it.apply {
            forecast.forecastday.forEach {
                it.dayOfWeek = dateUtilImpl.format(it.date, DateUtils.FOR_YYYY_H_MM_H_DD, FULL_DAY)
            }
        }
    }
}

fun handleError(error: Exception) {
    loadingState.value = false
    _error.value = error.localizedMessage
}

fun loadWeatherForeCast(latitude: Double, longitude: Double) {
    val weatherParam = WeatherParam(location = String.format(Locale.getDefault(), "%1f, %2f",
        latitude, longitude))

    weatherUseCase.execute(this, weatherParam)
    {
        when (it) {
            is Result.Success -> {
                loadingState.value = false
                _locationLiveData.value = it.data
            }
            is Result.Error -> {
                handleError(it.exception)
            }
        }
    }
}

override fun onCleared() {
    job.cancel()
    super.onCleared()
}

}

1 个答案:

答案 0 :(得分:0)

要将mockK与协程一起使用,您需要使用新的“ coEvery”和“ coVerify”函数,它将更好地工作:

https://mockk.io/#coroutines