我想在 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()
}
}