1

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

}

4

1 回答 1

0

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

https://mockk.io/#coroutines

于 2019-04-28T11:33:09.277 回答