Я пытаюсь реализовать модульное тестирование для своего приложения. У меня есть ViewModel, которая принимает варианты использования в качестве параметров, введенных в конструктор ниже
@HiltViewModel
class WeatherViewModel @Inject constructor(
private val searchWeatherUseCase: SearchWeatherUseCase,
) : ViewModel() {
private val _uiState: MutableStateFlow =
MutableStateFlow(WeatherUiState(isLoading = true))
val uiState: StateFlow = _uiState.asStateFlow()
private val _searchWidgetState: MutableState =
mutableStateOf(value = SearchWidgetState.CLOSED)
val searchWidgetState: State = _searchWidgetState
private val _searchTextState: MutableState = mutableStateOf(value = "")
val searchTextState: State = _searchTextState
fun updateSearchWidgetState(newValue: SearchWidgetState) {
_searchWidgetState.value = newValue
}
fun updateSearchTextState(newValue: String) {
_searchTextState.value = newValue
}
init {
getWeather()
}
fun getWeather(city: String = DEFAULT_WEATHER_DESTINATION) {
searchWeatherUseCase.getCities(city).map { result ->
when (result) {
is Result.Success -> {
val apiData = result.data
val isFavorite = data.find { apiData.name == it.name }
_uiState.value = WeatherUiState(
weather = apiData,
)
}
is Result.Error -> {
_uiState.value = WeatherUiState(errorMessage = result.errorMessage)
}
Result.Loading -> {
_uiState.value = WeatherUiState(isLoading = true)
}
}
}.launchIn(viewModelScope)
}
Затем запрос передается в UseCase, а затем
class SearchWeatherUseCase @Inject constructor(private val repositoryImpl: WeatherRepositoryImpl) {
fun getCities(cityName: String): Flow {
return repositoryImpl.findCityWeather(cityName)
}
}
который затем передает запрос классу реализации:
class WeatherRepositoryImpl @Inject constructor(
private val remoteSource: RemoteSource,
private val localSource: LocalSource,
private val dispatcher: CoroutineDispatcher = Dispatchers.IO,
) : WeatherRepository {
override fun findCityWeather(city: String): Flow = flow {
emit(Result.Loading)
try {
val result = remoteSource.findCityWeatherData(city)
emit(Result.Success(result))
} catch (exception: HttpException) {
emit(Result.Error(exception.message.orEmpty()))
} catch (exception: IOException) {
emit(Result.Error("Please check your network connection and try again!"))
}
}.flowOn(dispatcher)
override suspend fun saveFavCity(favouriteCity: Weather) = localSource.saveCity(favouriteCity)
override suspend fun getFavCities(): List {
return localSource.getSaveCities()
}
override suspend fun deleteFavCity(city: String) = localSource.deleteFavCity(city)
override suspend fun fetchFavouriteWeatherByName(name: String): Weather =
localSource.fetchFavouriteWeatherByName(name)
}
который решает, должны ли данные поступать из удаленных или локальных источников. В моем случае это удаленный источник, а именно:
class RemoteSource @Inject constructor(private val api: ApiInterface) {
suspend fun findCityWeatherData(city: String): Weather {
return api.findCityWeatherData(q = city).toWeather()
}
}
Это процесс передачи/испускания данных. А ниже мой WeatherViewModelTest:
@ExperimentalCoroutinesApi
class WeatherViewModelTest {
// Rule to handle LiveData synchronization
@get:Rule
val instantTaskExecutorRule = InstantTaskExecutorRule()
// Use MockK to mock use cases
private lateinit var searchWeatherUseCase: SearchWeatherUseCase
private lateinit var saveCityUseCase: SaveCityUseCase
private lateinit var getSaveCityUseCase: GetSaveCityUseCase
private lateinit var deleteSaveCityUseCase: DeleteSaveCityUseCase
private lateinit var getFavouriteCityUseCase: GetFavouriteCityUseCase
// ViewModel under test
private lateinit var weatherViewModel: WeatherViewModel
// Test Dispatcher
private val testDispatcher = StandardTestDispatcher()
@Before
fun setup() {
// Mock the use cases
searchWeatherUseCase = mockk()
getSaveCityUseCase = mockk()
saveCityUseCase = mockk()
deleteSaveCityUseCase = mockk()
// Initialize ViewModel with the mocked use cases
weatherViewModel = WeatherViewModel(
searchWeatherUseCase,
saveCityUseCase,
getSaveCityUseCase,
deleteSaveCityUseCase
)
// Set up common behavior for saving cities
coEvery { getSaveCityUseCase.getSaveCities() } returns listOf(
Weather(
id = 1L,
temperature = 25,
date = "2023-09-25",
wind = 10,
humidity = 60,
feelsLike = 24,
uv = 5,
name = "Cottbus",
isFavourite = true
)
)
}
@Test
fun `getWeather() should update uiState with weather data when result is success`() = runTest {
// Given a successful weather fetch result
val weather = Weather(
id = 1L,
temperature = 25,
date = "2023-09-25",
wind = 10,
humidity = 60,
feelsLike = 24,
uv = 5,
name = "Cottbus",
isFavourite = true
)
// Mock the behavior of searchWeatherUseCase.getCities for "Cottbus"
coEvery { searchWeatherUseCase.getCities("Cottbus") } returns flowOf(Result.Success(weather))
// When fetching weather for a city
weatherViewModel.getWeather("Cottbus")
// Run the pending coroutines
advanceUntilIdle()
// Verify that the getCities method was called with "Cottbus"
coVerify { searchWeatherUseCase.getCities("Cottbus") }
// Assert that the ViewModel updates the UI state correctly
val expectedUiState = WeatherUiState(
weather = weather,
favouriteCity = listOf(weather),
isFavorite = true
)
assertEquals(expectedUiState, weatherViewModel.uiState.value)
}
@After
fun tearDown() {
Dispatchers.resetMain() // Reset the dispatcher after the test
}
@Test
fun `getWeather() should update uiState with error when result is failure`() = runTest {
// Given a failure weather fetch result
val errorMessage = "Network error"
coEvery { searchWeatherUseCase.getCities(any()) } returns flowOf(Result.Error(errorMessage))
// When fetching weather for a city
weatherViewModel.getWeather("Test City")
// Run the pending coroutines
advanceUntilIdle()
// Then assert that the UI state is updated with an error message
assertEquals(errorMessage, weatherViewModel.uiState.value.errorMessage)
}
}
Тест не пройден и выдает следующую ошибку:
no answer found for SearchWeatherUseCase(#1).getCities(Cottbus) among the configured answers: ()
io.mockk.MockKException: no answer found for SearchWeatherUseCase(#1).getCities(Cottbus) among the configured answers: ( ) < b r / > a t a p p / / i o . m o c k k . i m p l . s t u b . M o c k K S t u b . d e f a u l t A n s w e r ( M o c k K S t u b . k t : 9 1 ) < b r / > a t a p p / / i o . m o c k k . i m p l . s t u b . M o c k K S t u b . a n s w e r ( M o c k K S t u b . k t : 4 2 ) < b r / > a t a p p / / i o . m o c k k . i m p l . r e c o r d i n g . s t a t e s . A n s w e r i n g S t a t e . c a l l ( A n s w e r i n g S t a t e . k t : 1 6 ) < b r / > a t a p p / / i o . m o c k k . i m p l . r e c o r d i n g . C o m m o n C a l l R e c o r d e r . c a l l ( C o m m o n C a l l R e c o r d e r . k t : 5 3 ) < b r / > a t a p p / / i o . m o c k k . i m p l . s t u b . M o c k K S t u b . h a n d l e I n v o c a t i o n ( M o c k K S t u b . k t : 2 6 9 ) < b r / > a t a p p / / i o . m o c k k . i m p l . i n s t a n t i a t i o n . J v m M o c k F a c t o r y H e l p e r $ m o c k H a n d l e r $ 1 . i n v o c a t i o n ( J v m M o c k F a c t o r y H e l p e r . k t : 2 4 ) < b r / > a t a p p / / i o . m o c k k . p r o x y . j v m . a d v i c e . I n t e r c e p t o r . c a l l ( I n t e r c e p t o r . k t : 2 1 ) < b r / > a t a p p / / c o m . e x a m p l e . w e a t h e r v i e w e r . d a t a . u s e c a s e . S e a r c h W e a t h e r U s e C a s e . g e t C i t i e s ( S e a r c h W e a t h e r U s e C a s e . k t : 1 1 ) < b r / > a t a p p / / c o m . e x a m p l e . w e a t h e r v i e w e r . u i . v i e w m o d e l s . W e a t h e r V i e w M o d e l . g e t W e a t h e r ( W e a t h e r V i e w M o d e l . k t : 6 5 ) < b r / > a t a p p / / c o m . e x a m p l e . w e a t h e r v i e w e r . u i . v i e w m o d e l s . W e a t h erViewModel.getWeather$default(WeatherViewModel.kt:64)
at app//com.example.weatherviewer.ui.viewmodels.WeatherViewModel.(WeatherViewModel.kt:60)
at app//WeatherViewModelTest.setup(WeatherViewModelTest.kt:56)
at java.base@17.0.11/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base@17.0.11/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
at java.base@17.0.11/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base@17.0.11/java.lang.reflect.Method.invoke(Method.java:568)
at app//org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59)
at app//org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at app//org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)
at app//org.junit.internal.runners.statements.RunBefores.invokeMethod(RunBefores.java:33)
at app//org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:24)
at app//org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:27)
at app//org.junit.rules.TestWatcher$1.evaluate(TestWatcher.java:61)
at app//org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
at app//org.junit.runners.BlockJUnit4ClassRunner$1.evaluate(BlockJUnit4ClassRunner.java:100)
at app//org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
at app//org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:103)
at app//org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:63)
at app//org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
at app//org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
at app//org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
at app//org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
at app//org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
at app//org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
at app//org.junit.runners.ParentRunner.run(ParentRunner.java:413)
at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.runTestClass(JUnitTestClassExecutor.java:112)
at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.execute(JUnitTestClassExecutor.java:58)
at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.execute(JUnitTestClassExecutor.java:40)
at org.gradle.api.internal.tasks.testing.junit.AbstractJUnitTestClassProcessor.processTestClass(AbstractJUnitTestClassProcessor.java:60)
at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.processTestClass(SuiteTestClassProcessor.java:52)
at java.base@17.0.11/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base@17.0.11/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
at java.base@17.0.11/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base@17.0.11/java.lang.reflect.Method.invoke(Method.java:568)
at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:36)
at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
at org.gradle.internal.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:33)
at org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:94)
at jdk.proxy1/jdk.proxy1.$Proxy2.processTestClass(Unknown Source)
at org.gradle.api.internal.tasks.testing.worker.TestWorker$2.run(TestWorker.java:176)
at org.gradle.api.internal.tasks.testing.worker.TestWorker.executeAndMaintainThreadName(TestWorker.java:129)
at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:100)
at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:60)
at org.gradle.process.internal.worker.child.ActionExecutionWorker.execute(ActionExecutionWorker.java:56)
at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:113)
at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:65)
at app//worker.org.gradle.process.internal.worker.GradleWorkerMain.run(GradleWorkerMain.java:69)
at app//worker.org.gradle.process.internal.worker.GradleWorkerMain.main(GradleWorkerMain.java:74)
no answer found for SearchWeatherUseCase(#5).getCities(Cottbus) among the configured answers: ()
io.mockk.MockKException: no answer found for SearchWeatherUseCase(#5).getCities(Cottbus) among the configured answers: ( ) < b r / > a t a p p / / i o . m o c k k . i m p l . s t u b . M o c k K S t u b . d e f a u l t A n s w e r ( M o c k K S t u b . k t : 9 1 ) < b r / > a t a p p / / i o . m o c k k . i m p l . s t u b . M o c k K S t u b . a n s w e r ( M o c k K S t u b . k t : 4 2 ) < b r / > a t a p p / / i o . m o c k k . i m p l . r e c o r d i n g . s t a t e s . A n s w e r i n g S t a t e . c a l l ( A n s w e r i n g S t a t e . k t : 1 6 ) < b r / > a t a p p / / i o . m o c k k . i m p l . r e c o r d i n g . C o m m o n C a l l R e c o r d e r . c a l l ( C o m m o n C a l l R e c o r d e r . k t : 5 3 ) < b r / > a t a p p / / i o . m o c k k . i m p l . s t u b . M o c k K S t u b . h a n d l e I n v o c a t i o n ( M o c k K S t u b . k t : 2 6 9 ) < b r / > a t a p p / / i o . m o c k k . i m p l . i n s t a n t i a t i o n . J v m M o c k F a c t o r y H e l p e r $ m o c k H a n d l e r $ 1 . i n v o c a t i o n ( J v m M o c k F a c t o r y H e l p e r . k t : 2 4 ) < b r / > a t a p p / / i o . m o c k k . p r o x y . j v m . a d v i c e . I n t e r c e p t o r . c a l l ( I n t e r c e p t o r . k t : 2 1 ) < b r / > a t a p p / / c o m . e x a m p l e . w e a t h e r v i e w e r . d a t a . u s e c a s e . S e a r c h W e a t h e r U s e C a s e . g e t C i t i e s ( S e a r c h W e a t h e r U s e C a s e . k t : 1 1 ) < b r / > a t a p p / / c o m . e x a m p l e . w e a t h e r v i e w e r . u i . v i e w m o d e l s . W e a t h e r V i e w M o d e l . g e t W e a t h e r ( W e a t h e r V i e w M o d e l .kt:65)
at app//com.example.weatherviewer.ui.viewmodels.WeatherViewModel.getWeather$default(WeatherViewModel.kt:64)
at app//com.example.weatherviewer.ui.viewmodels.WeatherViewModel.(WeatherViewModel.kt:60)
at app//WeatherViewModelTest.setup(WeatherViewModelTest.kt:56)
at java.base@17.0.11/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base@17.0.11/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
at java.base@17.0.11/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base@17.0.11/java.lang.reflect.Method.invoke(Method.java:568)
at app//org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59)
at app//org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at app//org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)
at app//org.junit.internal.runners.statements.RunBefores.invokeMethod(RunBefores.java:33)
at app//org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:24)
at app//org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:27)
at app//org.junit.rules.TestWatcher$1.evaluate(TestWatcher.java:61)
at app//org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
at app//org.junit.runners.BlockJUnit4ClassRunner$1.evaluate(BlockJUnit4ClassRunner.java:100)
at app//org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
at app//org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:103)
at app//org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:63)
at app//org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
at app//org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
at app//org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
at app//org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
at app//org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
at app//org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
at app//org.junit.runners.ParentRunner.run(ParentRunner.java:413)
at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.runTestClass(JUnitTestClassExecutor.java:112)
at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.execute(JUnitTestClassExecutor.java:58)
at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.execute(JUnitTestClassExecutor.java:40)
at org.gradle.api.internal.tasks.testing.junit.AbstractJUnitTestClassProcessor.processTestClass(AbstractJUnitTestClassProcessor.java:60)
at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.processTestClass(SuiteTestClassProcessor.java:52)
at java.base@17.0.11/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base@17.0.11/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
at java.base@17.0.11/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base@17.0.11/java.lang.reflect.Method.invoke(Method.java:568)
at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:36)
at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
at org.gradle.internal.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:33)
at org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:94)
at jdk.proxy1/jdk.proxy1.$Proxy2.processTestClass(Unknown Source)
at org.gradle.api.internal.tasks.testing.worker.TestWorker$2.run(TestWorker.java:176)
at org.gradle.api.internal.tasks.testing.worker.TestWorker.executeAndMaintainThreadName(TestWorker.java:129)
at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:100)
at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:60)
at org.gradle.process.internal.worker.child.ActionExecutionWorker.execute(ActionExecutionWorker.java:56)
at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:113)
at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:65)
at app//worker.org.gradle.process.internal.worker.GradleWorkerMain.run(GradleWorkerMain.java:69)
at app//worker.org.gradle.process.internal.worker.GradleWorkerMain.main(GradleWorkerMain.java:74)
Подробнее здесь: https://stackoverflow.com/questions/790 ... swers-io-m
Ответ для SearchWeatherUseCase не найден (# 1 среди настроенных ответов: () io.mockk.MockKException: ответ не найден для ⇐ Android
Форум для тех, кто программирует под Android
1727231745
Anonymous
Я пытаюсь реализовать модульное тестирование для своего приложения. У меня есть ViewModel, которая принимает варианты использования в качестве параметров, введенных в конструктор ниже
@HiltViewModel
class WeatherViewModel @Inject constructor(
private val searchWeatherUseCase: SearchWeatherUseCase,
) : ViewModel() {
private val _uiState: MutableStateFlow =
MutableStateFlow(WeatherUiState(isLoading = true))
val uiState: StateFlow = _uiState.asStateFlow()
private val _searchWidgetState: MutableState =
mutableStateOf(value = SearchWidgetState.CLOSED)
val searchWidgetState: State = _searchWidgetState
private val _searchTextState: MutableState = mutableStateOf(value = "")
val searchTextState: State = _searchTextState
fun updateSearchWidgetState(newValue: SearchWidgetState) {
_searchWidgetState.value = newValue
}
fun updateSearchTextState(newValue: String) {
_searchTextState.value = newValue
}
init {
getWeather()
}
fun getWeather(city: String = DEFAULT_WEATHER_DESTINATION) {
searchWeatherUseCase.getCities(city).map { result ->
when (result) {
is Result.Success -> {
val apiData = result.data
val isFavorite = data.find { apiData.name == it.name }
_uiState.value = WeatherUiState(
weather = apiData,
)
}
is Result.Error -> {
_uiState.value = WeatherUiState(errorMessage = result.errorMessage)
}
Result.Loading -> {
_uiState.value = WeatherUiState(isLoading = true)
}
}
}.launchIn(viewModelScope)
}
Затем запрос передается в UseCase, а затем
class SearchWeatherUseCase @Inject constructor(private val repositoryImpl: WeatherRepositoryImpl) {
fun getCities(cityName: String): Flow {
return repositoryImpl.findCityWeather(cityName)
}
}
который затем передает запрос классу реализации:
class WeatherRepositoryImpl @Inject constructor(
private val remoteSource: RemoteSource,
private val localSource: LocalSource,
private val dispatcher: CoroutineDispatcher = Dispatchers.IO,
) : WeatherRepository {
override fun findCityWeather(city: String): Flow = flow {
emit(Result.Loading)
try {
val result = remoteSource.findCityWeatherData(city)
emit(Result.Success(result))
} catch (exception: HttpException) {
emit(Result.Error(exception.message.orEmpty()))
} catch (exception: IOException) {
emit(Result.Error("Please check your network connection and try again!"))
}
}.flowOn(dispatcher)
override suspend fun saveFavCity(favouriteCity: Weather) = localSource.saveCity(favouriteCity)
override suspend fun getFavCities(): List {
return localSource.getSaveCities()
}
override suspend fun deleteFavCity(city: String) = localSource.deleteFavCity(city)
override suspend fun fetchFavouriteWeatherByName(name: String): Weather =
localSource.fetchFavouriteWeatherByName(name)
}
который решает, должны ли данные поступать из удаленных или локальных источников. В моем случае это удаленный источник, а именно:
class RemoteSource @Inject constructor(private val api: ApiInterface) {
suspend fun findCityWeatherData(city: String): Weather {
return api.findCityWeatherData(q = city).toWeather()
}
}
Это процесс передачи/испускания данных. А ниже мой WeatherViewModelTest:
@ExperimentalCoroutinesApi
class WeatherViewModelTest {
// Rule to handle LiveData synchronization
@get:Rule
val instantTaskExecutorRule = InstantTaskExecutorRule()
// Use MockK to mock use cases
private lateinit var searchWeatherUseCase: SearchWeatherUseCase
private lateinit var saveCityUseCase: SaveCityUseCase
private lateinit var getSaveCityUseCase: GetSaveCityUseCase
private lateinit var deleteSaveCityUseCase: DeleteSaveCityUseCase
private lateinit var getFavouriteCityUseCase: GetFavouriteCityUseCase
// ViewModel under test
private lateinit var weatherViewModel: WeatherViewModel
// Test Dispatcher
private val testDispatcher = StandardTestDispatcher()
@Before
fun setup() {
// Mock the use cases
searchWeatherUseCase = mockk()
getSaveCityUseCase = mockk()
saveCityUseCase = mockk()
deleteSaveCityUseCase = mockk()
// Initialize ViewModel with the mocked use cases
weatherViewModel = WeatherViewModel(
searchWeatherUseCase,
saveCityUseCase,
getSaveCityUseCase,
deleteSaveCityUseCase
)
// Set up common behavior for saving cities
coEvery { getSaveCityUseCase.getSaveCities() } returns listOf(
Weather(
id = 1L,
temperature = 25,
date = "2023-09-25",
wind = 10,
humidity = 60,
feelsLike = 24,
uv = 5,
name = "Cottbus",
isFavourite = true
)
)
}
@Test
fun `getWeather() should update uiState with weather data when result is success`() = runTest {
// Given a successful weather fetch result
val weather = Weather(
id = 1L,
temperature = 25,
date = "2023-09-25",
wind = 10,
humidity = 60,
feelsLike = 24,
uv = 5,
name = "Cottbus",
isFavourite = true
)
// Mock the behavior of searchWeatherUseCase.getCities for "Cottbus"
coEvery { searchWeatherUseCase.getCities("Cottbus") } returns flowOf(Result.Success(weather))
// When fetching weather for a city
weatherViewModel.getWeather("Cottbus")
// Run the pending coroutines
advanceUntilIdle()
// Verify that the getCities method was called with "Cottbus"
coVerify { searchWeatherUseCase.getCities("Cottbus") }
// Assert that the ViewModel updates the UI state correctly
val expectedUiState = WeatherUiState(
weather = weather,
favouriteCity = listOf(weather),
isFavorite = true
)
assertEquals(expectedUiState, weatherViewModel.uiState.value)
}
@After
fun tearDown() {
Dispatchers.resetMain() // Reset the dispatcher after the test
}
@Test
fun `getWeather() should update uiState with error when result is failure`() = runTest {
// Given a failure weather fetch result
val errorMessage = "Network error"
coEvery { searchWeatherUseCase.getCities(any()) } returns flowOf(Result.Error(errorMessage))
// When fetching weather for a city
weatherViewModel.getWeather("Test City")
// Run the pending coroutines
advanceUntilIdle()
// Then assert that the UI state is updated with an error message
assertEquals(errorMessage, weatherViewModel.uiState.value.errorMessage)
}
}
Тест не пройден и выдает следующую ошибку:
no answer found for SearchWeatherUseCase(#1).getCities(Cottbus) among the configured answers: ()
io.mockk.MockKException: no answer found for SearchWeatherUseCase(#1).getCities(Cottbus) among the configured answers: ( ) < b r / > a t a p p / / i o . m o c k k . i m p l . s t u b . M o c k K S t u b . d e f a u l t A n s w e r ( M o c k K S t u b . k t : 9 1 ) < b r / > a t a p p / / i o . m o c k k . i m p l . s t u b . M o c k K S t u b . a n s w e r ( M o c k K S t u b . k t : 4 2 ) < b r / > a t a p p / / i o . m o c k k . i m p l . r e c o r d i n g . s t a t e s . A n s w e r i n g S t a t e . c a l l ( A n s w e r i n g S t a t e . k t : 1 6 ) < b r / > a t a p p / / i o . m o c k k . i m p l . r e c o r d i n g . C o m m o n C a l l R e c o r d e r . c a l l ( C o m m o n C a l l R e c o r d e r . k t : 5 3 ) < b r / > a t a p p / / i o . m o c k k . i m p l . s t u b . M o c k K S t u b . h a n d l e I n v o c a t i o n ( M o c k K S t u b . k t : 2 6 9 ) < b r / > a t a p p / / i o . m o c k k . i m p l . i n s t a n t i a t i o n . J v m M o c k F a c t o r y H e l p e r $ m o c k H a n d l e r $ 1 . i n v o c a t i o n ( J v m M o c k F a c t o r y H e l p e r . k t : 2 4 ) < b r / > a t a p p / / i o . m o c k k . p r o x y . j v m . a d v i c e . I n t e r c e p t o r . c a l l ( I n t e r c e p t o r . k t : 2 1 ) < b r / > a t a p p / / c o m . e x a m p l e . w e a t h e r v i e w e r . d a t a . u s e c a s e . S e a r c h W e a t h e r U s e C a s e . g e t C i t i e s ( S e a r c h W e a t h e r U s e C a s e . k t : 1 1 ) < b r / > a t a p p / / c o m . e x a m p l e . w e a t h e r v i e w e r . u i . v i e w m o d e l s . W e a t h e r V i e w M o d e l . g e t W e a t h e r ( W e a t h e r V i e w M o d e l . k t : 6 5 ) < b r / > a t a p p / / c o m . e x a m p l e . w e a t h e r v i e w e r . u i . v i e w m o d e l s . W e a t h erViewModel.getWeather$default(WeatherViewModel.kt:64)
at app//com.example.weatherviewer.ui.viewmodels.WeatherViewModel.(WeatherViewModel.kt:60)
at app//WeatherViewModelTest.setup(WeatherViewModelTest.kt:56)
at java.base@17.0.11/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base@17.0.11/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
at java.base@17.0.11/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base@17.0.11/java.lang.reflect.Method.invoke(Method.java:568)
at app//org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59)
at app//org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at app//org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)
at app//org.junit.internal.runners.statements.RunBefores.invokeMethod(RunBefores.java:33)
at app//org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:24)
at app//org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:27)
at app//org.junit.rules.TestWatcher$1.evaluate(TestWatcher.java:61)
at app//org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
at app//org.junit.runners.BlockJUnit4ClassRunner$1.evaluate(BlockJUnit4ClassRunner.java:100)
at app//org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
at app//org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:103)
at app//org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:63)
at app//org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
at app//org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
at app//org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
at app//org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
at app//org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
at app//org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
at app//org.junit.runners.ParentRunner.run(ParentRunner.java:413)
at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.runTestClass(JUnitTestClassExecutor.java:112)
at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.execute(JUnitTestClassExecutor.java:58)
at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.execute(JUnitTestClassExecutor.java:40)
at org.gradle.api.internal.tasks.testing.junit.AbstractJUnitTestClassProcessor.processTestClass(AbstractJUnitTestClassProcessor.java:60)
at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.processTestClass(SuiteTestClassProcessor.java:52)
at java.base@17.0.11/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base@17.0.11/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
at java.base@17.0.11/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base@17.0.11/java.lang.reflect.Method.invoke(Method.java:568)
at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:36)
at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
at org.gradle.internal.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:33)
at org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:94)
at jdk.proxy1/jdk.proxy1.$Proxy2.processTestClass(Unknown Source)
at org.gradle.api.internal.tasks.testing.worker.TestWorker$2.run(TestWorker.java:176)
at org.gradle.api.internal.tasks.testing.worker.TestWorker.executeAndMaintainThreadName(TestWorker.java:129)
at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:100)
at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:60)
at org.gradle.process.internal.worker.child.ActionExecutionWorker.execute(ActionExecutionWorker.java:56)
at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:113)
at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:65)
at app//worker.org.gradle.process.internal.worker.GradleWorkerMain.run(GradleWorkerMain.java:69)
at app//worker.org.gradle.process.internal.worker.GradleWorkerMain.main(GradleWorkerMain.java:74)
no answer found for SearchWeatherUseCase(#5).getCities(Cottbus) among the configured answers: ()
io.mockk.MockKException: no answer found for SearchWeatherUseCase(#5).getCities(Cottbus) among the configured answers: ( ) < b r / > a t a p p / / i o . m o c k k . i m p l . s t u b . M o c k K S t u b . d e f a u l t A n s w e r ( M o c k K S t u b . k t : 9 1 ) < b r / > a t a p p / / i o . m o c k k . i m p l . s t u b . M o c k K S t u b . a n s w e r ( M o c k K S t u b . k t : 4 2 ) < b r / > a t a p p / / i o . m o c k k . i m p l . r e c o r d i n g . s t a t e s . A n s w e r i n g S t a t e . c a l l ( A n s w e r i n g S t a t e . k t : 1 6 ) < b r / > a t a p p / / i o . m o c k k . i m p l . r e c o r d i n g . C o m m o n C a l l R e c o r d e r . c a l l ( C o m m o n C a l l R e c o r d e r . k t : 5 3 ) < b r / > a t a p p / / i o . m o c k k . i m p l . s t u b . M o c k K S t u b . h a n d l e I n v o c a t i o n ( M o c k K S t u b . k t : 2 6 9 ) < b r / > a t a p p / / i o . m o c k k . i m p l . i n s t a n t i a t i o n . J v m M o c k F a c t o r y H e l p e r $ m o c k H a n d l e r $ 1 . i n v o c a t i o n ( J v m M o c k F a c t o r y H e l p e r . k t : 2 4 ) < b r / > a t a p p / / i o . m o c k k . p r o x y . j v m . a d v i c e . I n t e r c e p t o r . c a l l ( I n t e r c e p t o r . k t : 2 1 ) < b r / > a t a p p / / c o m . e x a m p l e . w e a t h e r v i e w e r . d a t a . u s e c a s e . S e a r c h W e a t h e r U s e C a s e . g e t C i t i e s ( S e a r c h W e a t h e r U s e C a s e . k t : 1 1 ) < b r / > a t a p p / / c o m . e x a m p l e . w e a t h e r v i e w e r . u i . v i e w m o d e l s . W e a t h e r V i e w M o d e l . g e t W e a t h e r ( W e a t h e r V i e w M o d e l .kt:65)
at app//com.example.weatherviewer.ui.viewmodels.WeatherViewModel.getWeather$default(WeatherViewModel.kt:64)
at app//com.example.weatherviewer.ui.viewmodels.WeatherViewModel.(WeatherViewModel.kt:60)
at app//WeatherViewModelTest.setup(WeatherViewModelTest.kt:56)
at java.base@17.0.11/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base@17.0.11/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
at java.base@17.0.11/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base@17.0.11/java.lang.reflect.Method.invoke(Method.java:568)
at app//org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59)
at app//org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at app//org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)
at app//org.junit.internal.runners.statements.RunBefores.invokeMethod(RunBefores.java:33)
at app//org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:24)
at app//org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:27)
at app//org.junit.rules.TestWatcher$1.evaluate(TestWatcher.java:61)
at app//org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
at app//org.junit.runners.BlockJUnit4ClassRunner$1.evaluate(BlockJUnit4ClassRunner.java:100)
at app//org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
at app//org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:103)
at app//org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:63)
at app//org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
at app//org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
at app//org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
at app//org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
at app//org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
at app//org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
at app//org.junit.runners.ParentRunner.run(ParentRunner.java:413)
at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.runTestClass(JUnitTestClassExecutor.java:112)
at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.execute(JUnitTestClassExecutor.java:58)
at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.execute(JUnitTestClassExecutor.java:40)
at org.gradle.api.internal.tasks.testing.junit.AbstractJUnitTestClassProcessor.processTestClass(AbstractJUnitTestClassProcessor.java:60)
at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.processTestClass(SuiteTestClassProcessor.java:52)
at java.base@17.0.11/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base@17.0.11/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
at java.base@17.0.11/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base@17.0.11/java.lang.reflect.Method.invoke(Method.java:568)
at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:36)
at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
at org.gradle.internal.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:33)
at org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:94)
at jdk.proxy1/jdk.proxy1.$Proxy2.processTestClass(Unknown Source)
at org.gradle.api.internal.tasks.testing.worker.TestWorker$2.run(TestWorker.java:176)
at org.gradle.api.internal.tasks.testing.worker.TestWorker.executeAndMaintainThreadName(TestWorker.java:129)
at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:100)
at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:60)
at org.gradle.process.internal.worker.child.ActionExecutionWorker.execute(ActionExecutionWorker.java:56)
at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:113)
at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:65)
at app//worker.org.gradle.process.internal.worker.GradleWorkerMain.run(GradleWorkerMain.java:69)
at app//worker.org.gradle.process.internal.worker.GradleWorkerMain.main(GradleWorkerMain.java:74)
Подробнее здесь: [url]https://stackoverflow.com/questions/79020664/no-answer-found-for-searchweatherusecase1among-the-configured-answers-io-m[/url]
-
- Похожие темы
- Ответы
- Просмотры
- Последнее сообщение
-
-
Ответ для SearchWeatherUseCase не найден (# 1 среди настроенных ответов: () io.mockk.MockKException: ответ не найден для
Anonymous » » в форуме AndroidЯ пытаюсь реализовать модульное тестирование для своего приложения. У меня есть ViewModel, которая принимает варианты использования в качестве параметров, введенных в конструктор ниже
@HiltViewModel
class WeatherViewModel @Inject constructor(... - 0 Ответы
- 11 Просмотры
-
Последнее сообщение Anonymous
-
-
-
Существует ли конкретное руководство по обновлению RHEL через Leap для узлов, настроенных как кластер citus? [закрыто]
Anonymous » » в форуме LinuxМы пытаемся обновить нашу ОС с RHEL 7.9 до RHEL 8.9. Однако в среде разработки утилита Leap полностью удалила postgres. Наш администратор ОС говорит, что проблема в том, что мы не установили postgress из репозитория RHEL. В производстве у нас есть... - 0 Ответы
- 22 Просмотры
-
Последнее сообщение Anonymous
-
-
-
Список наборов шифров, настроенных для Tomcat 9 в Java
Anonymous » » в форуме JAVAЯ пишу Java-приложение, использующее SSLSocket на сервере Tomcat 9.
По умолчанию SSLSocket принимает комплекты шифров на основе Java JDK. Чтобы пользователи могли отключать слабые комплекты шифров при использовании SSLSocket, я хочу включить только... - 0 Ответы
- 15 Просмотры
-
Последнее сообщение Anonymous
-
-
-
Как изменить цвет настроенных кривых при использовании анимации в Python matplotlib?
Anonymous » » в форуме PythonУ меня есть кусок кода, который использует метод Funcanimation в Python matplotlib, чтобы генерировать 50 случайных экспоненциальных кривых затухания и обновления графика, показывающего друг друга, кривые, как они сгенерировали. У вас появляются... - 0 Ответы
- 15 Просмотры
-
Последнее сообщение Anonymous
-
-
-
Удаленная форма Rails UJS оценивает JS только для ответов 2XX, но не для ответов 4XX
Anonymous » » в форуме JqueryБыли ли изменения в том, как JQuery 3 и Jquery 1 обрабатывали ошибки удаленной формы в Rails?
Раньше (Rails 6, Zurb Foundation, Jquery 1) код ниже должно сработать. Пользователь отправлял форму, забывал свое имя, мы устанавливали переменную... - 0 Ответы
- 59 Просмотры
-
Последнее сообщение Anonymous
-
Перейти
- Кемерово-IT
- ↳ Javascript
- ↳ C#
- ↳ JAVA
- ↳ Elasticsearch aggregation
- ↳ Python
- ↳ Php
- ↳ Android
- ↳ Html
- ↳ Jquery
- ↳ C++
- ↳ IOS
- ↳ CSS
- ↳ Excel
- ↳ Linux
- ↳ Apache
- ↳ MySql
- Детский мир
- Для души
- ↳ Музыкальные инструменты даром
- ↳ Печатная продукция даром
- Внешняя красота и здоровье
- ↳ Одежда и обувь для взрослых даром
- ↳ Товары для здоровья
- ↳ Физкультура и спорт
- Техника - даром!
- ↳ Автомобилистам
- ↳ Компьютерная техника
- ↳ Плиты: газовые и электрические
- ↳ Холодильники
- ↳ Стиральные машины
- ↳ Телевизоры
- ↳ Телефоны, смартфоны, плашеты
- ↳ Швейные машинки
- ↳ Прочая электроника и техника
- ↳ Фототехника
- Ремонт и интерьер
- ↳ Стройматериалы, инструмент
- ↳ Мебель и предметы интерьера даром
- ↳ Cантехника
- Другие темы
- ↳ Разное даром
- ↳ Давай меняться!
- ↳ Отдам\возьму за копеечку
- ↳ Работа и подработка в Кемерове
- ↳ Давай с тобой поговорим...