Тесты проходят, когда я запускаю их по отдельности, но когда я запускаю их все, проходит только первый, а остальные терпят неудачу с нулевыми данными. не работает.
Я публикую как можно больше информации ниже. Не знаю, почему после первого теста liveata обнуляется.
gradle (app)
androidTestImplementation "androidx.test.ext:junit:1.1.1"
androidTestImplementation "androidx.test:runner:1.2.0"
androidTestImplementation "androidx.test:rules:1.2.0"
androidTestImplementation "androidx.test:core:1.2.0"
androidTestImplementation "androidx.test.espresso:espresso-core:$rootProject.espressoVersion"
androidTestImplementation "androidx.test.espresso:espresso-contrib:$rootProject.espressoVersion"
implementation "androidx.test.espresso:espresso-idling-resource:$rootProject.espressoVersion"
//Test Coroutines
testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:1.3.8"
//Junit 5
testImplementation "org.junit.jupiter:junit-jupiter-api:5.5.2"
testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:5.5.2"
androidTestRuntimeOnly "de.mannodermaus.junit5:android-test-runner:1.1.0"
//Test Rule Architecture Components
testImplementation "androidx.arch.core:core-testing:$rootProject.archTestVersion"
// MockK
testImplementation "io.mockk:mockk:1.9.3"
Tests
import io.mockk.coEvery
import io.mockk.mockk
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.test.TestCoroutineDispatcher
import kotlinx.coroutines.test.resetMain
import kotlinx.coroutines.test.setMain
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import java.io.IOException
@ExtendWith(InstantExecutorExtension::class)
class SearchViewModelTest {
private val dispatcher = TestCoroutineDispatcher()
private val tmdbApi = mockk<TmdbApi>()
private var viewModel: SearchViewModel? = null
private val movieTitle = "nemo"
private val successResponse = NetworkResponse.Success(
MovieResponseDTO(listOf(
MovieDTO(
false,
"backdrop-path",
arrayListOf("1", "2", "3", "4"),
arrayListOf("comedy, drama, adventure"),
1234,
"en-US",
"Finding Nemo",
"movie overview",
90.0,
"poster-path",
"2020",
"Finding Nemo",
false,
10.0,
200000
)
))
)
private val networkErrorResponse = NetworkResponse.NetworkError(IOException())
private val apiErrorResponse = NetworkResponse.ApiError(Error(500, "unavailable"), 500)
private val unknownErrorResponse = NetworkResponse.UnknownError(Throwable())
@BeforeEach
fun init() {
viewModel = SearchViewModel(tmdbApi, dispatcher)
Dispatchers.setMain(dispatcher)
}
@AfterEach
fun tearDown() {
viewModel = null
Dispatchers.resetMain()
dispatcher.cleanupTestCoroutines()
}
@Test
fun `make request, returns 200`() {
//Arrange
coEvery { tmdbApi.searchMovieSuspend(AppConstants.LANGUAGE, movieTitle, false) } answers {
successResponse
}
//Act
viewModel?.searchMovie(movieTitle)
//Assert
assertEquals(null, viewModel?.errorMessage?.value)
assertEquals(false, viewModel?.errorScreenVisibility?.value)
assertEquals(successResponse.body.results, viewModel?.searchResult?.value)
}
@Test
fun `make request, returns 400`() {
//Arrange
coEvery { tmdbApi.searchMovieSuspend(AppConstants.LANGUAGE, movieTitle, false) } answers {
networkErrorResponse
}
//Act
viewModel?.searchMovie(movieTitle)
//Assert
assertEquals(null, viewModel?.searchResult?.value)
assertEquals(true, viewModel?.errorScreenVisibility?.value)
assertEquals(AppConstants.NETWORK_ERROR_MESSAGE, viewModel?.errorMessage?.value)
}
@Test
fun `make request, returns 500`() {
//Arrange
coEvery { tmdbApi.searchMovieSuspend(AppConstants.LANGUAGE, movieTitle, false) } answers {
apiErrorResponse
}
//Act
viewModel?.searchMovie(movieTitle)
//Assert
assertEquals(null, viewModel?.searchResult?.value)
assertEquals(true, viewModel?.errorScreenVisibility?.value)
assertEquals(AppConstants.API_ERROR_MESSAGE, viewModel?.errorMessage?.value)
}
}
InstantExecutorExtension
import androidx.arch.core.executor.ArchTaskExecutor
import androidx.arch.core.executor.TaskExecutor
import org.junit.jupiter.api.extension.AfterEachCallback
import org.junit.jupiter.api.extension.BeforeAllCallback
import org.junit.jupiter.api.extension.ExtensionContext
class InstantExecutorExtension : BeforeAllCallback, AfterEachCallback {
override fun beforeAll(context: ExtensionContext?) {
ArchTaskExecutor.getInstance().setDelegate(object : TaskExecutor() {
override fun executeOnDiskIO(runnable: Runnable) {
runnable.run()
}
override fun isMainThread(): Boolean {
return true
}
override fun postToMainThread(runnable: Runnable) {
runnable.run()
}
})
}
override fun afterEach(context: ExtensionContext?) {
ArchTaskExecutor.getInstance().setDelegate(null)
}
}
ViewModel
class SearchViewModel @Inject constructor(private val tmdbApi: TmdbApi, @MainDispatcher val mainDispatcher: CoroutineDispatcher) : ViewModel() {
private var _searchResult = MutableLiveData<List<MovieDTO>>()
var searchResult: LiveData<List<MovieDTO>> = _searchResult
private var _searchProgressBarVisibility = MutableLiveData<Boolean>(false)
var searchProgressBarVisibility: LiveData<Boolean> = _searchProgressBarVisibility
private var _errorScreenVisibility = MutableLiveData<Boolean>(false)
var errorScreenVisibility: LiveData<Boolean> = _errorScreenVisibility
private var _noResultsVisibility = MutableLiveData<Boolean>(true)
var noResultsVisibility: LiveData<Boolean> = _noResultsVisibility
private var _errorMessage = MutableLiveData<String>()
var errorMessage: LiveData<String> = _errorMessage
val TAG = "calltest"
fun searchMovie(title: String){
showSearchProgressBar(true)
showError(false)
viewModelScope.launch(mainDispatcher) {
val response = tmdbApi.searchMovieSuspend(AppConstants.LANGUAGE, title, false)
showSearchProgressBar(false)
when (response) {
is NetworkResponse.Success -> {
Log.d(TAG, "Success")
_searchResult.value = response.body.results
showResults(response.body.results)
}
is NetworkResponse.ApiError -> {
Log.d("svmlog", "_____________________________")
Log.d("svmlog", "NetworkResponse.ApiError ->")
Log.d("svmlog", "code = ${response.body.cd}")
Log.d("svmlog", "msg = ${response.body.message}")
_errorMessage.value = AppConstants.API_ERROR_MESSAGE
showErrorScreen()
}
is NetworkResponse.NetworkError -> {
Log.d("svmlog", "_____________________________")
Log.d("svmlog", "NetworkResponse.NetworkError ->")
Log.d("svmlog", "cause = ${response.error.cause}")
Log.d("svmlog", "localizedMessage = ${response.error.localizedMessage}")
_errorMessage.value = AppConstants.NETWORK_ERROR_MESSAGE
showErrorScreen()
}
is NetworkResponse.UnknownError -> {
Log.d("svmlog", "_____________________________")
Log.d("svmlog", "NetworkResponse.UnknownError ->")
Log.d("svmlog", "cause = ${response.error?.cause}")
Log.d("svmlog", "localizedMessage = ${response.error?.localizedMessage}")
_errorMessage.value = AppConstants.UNKNOWN_ERROR_MESSAGE
showErrorScreen()
}
}
}
}
private fun showResults(results: List<MovieDTO>) {
if (results.isNullOrEmpty()){
showNoResultsMessage(true)
} else {
showNoResultsMessage(false)
}
}
private fun showErrorScreen() {
showError(true)
showNoResultsMessage(false)
}
private fun showError(shouldShow: Boolean) {
_errorScreenVisibility.value = shouldShow
}
private fun showSearchProgressBar(shouldShow: Boolean) {
_searchProgressBarVisibility.value = shouldShow
}
private fun showNoResultsMessage(shouldShow: Boolean) {
_noResultsVisibility.value = shouldShow
}
}
Журнал ошибок
java.lang.NullPointerException
at androidx.arch.core.executor.DefaultTaskExecutor.isMainThread(DefaultTaskExecutor.java:77)
at androidx.arch.core.executor.ArchTaskExecutor.isMainThread(ArchTaskExecutor.java:116)
at androidx.lifecycle.LiveData.assertMainThread(LiveData.java:461)
at androidx.lifecycle.LiveData.setValue(LiveData.java:304)
at androidx.lifecycle.MutableLiveData.setValue(MutableLiveData.java:50)
at com.movies.allmovies.viewmodel.SearchViewModel.showSearchProgressBar(SearchViewModel.kt:96)
at com.movies.allmovies.viewmodel.SearchViewModel.searchMovie(SearchViewModel.kt:38)
at com.movies.allmovies.viewmodel.SearchViewModelTest.make request, returns 400(SearchViewModelTest.kt:90)