android - 协程单元测试 Mockk java.lang.AbstractMethodError at kotlinx.coroutines.CoroutineContextKt.newCoroutineContext
问题描述
我想在 viewmodal 中对一个方法进行单元测试,但每次我都失败了,并且浏览了许多网站和堆栈答案,但没有一个有帮助。我只是想在我的 viewmodal 中测试一个方法,即 loadWeatherForeCast
我浏览了以下链接, 在单元测试中调用 Dispatchers.setMain() 时出错
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()
}
}
解决方案
要将 mockK 与协程一起使用,您需要使用新的“coEvery”和“coVerify”函数,它会更好地工作:
推荐阅读
- php - 从打开的文件句柄中获取路径信息
- r - 您如何将代码(如编码,编码)转换为编码字符串?
- html - SELECT OPTION 有条件地应用 CSS
- reactjs - 在打字稿模块中导入 ReactNode 失败
- python-3.x - 读取 csv 之类的多维数据数组,以便使用 sklearn 进行进一步处理
- angular - 不断收到 NullInjectorError: No provider for Injector!尝试与商店合作时
- javascript - 如何检查JS中的对象数组中是否存在属性
- c# - FileStreamResult 无法识别 Asp.Net Core 2.2 中的多个 http 范围
- sql - 将 Excel 数据导入 SQL Server
- c# - 在 featureActivated 中获取文件遇到错误:“值不在预期范围内”