Android Testing: Difference between revisions
Jump to navigation
Jump to search
Line 96: | Line 96: | ||
assertEquals(2, listViewModel.countries.value?.size) | assertEquals(2, listViewModel.countries.value?.size) | ||
assertEquals(false, listViewModel.countryLoadError.value) | assertEquals(false, listViewModel.countryLoadError.value) | ||
assertEquals(false, listViewModel.loading.value) | |||
} | |||
@Test | |||
fun getCountriesError() { | |||
testSingle = Single.error(Throwable( )) | |||
`when`(countriesService.getCountries()).thenReturn(testSingle) | |||
listViewModel.refresh() | |||
assertEquals(true, listViewModel.countryLoadError.value) | |||
assertEquals(false, listViewModel.loading.value) | assertEquals(false, listViewModel.loading.value) | ||
} | } |
Latest revision as of 00:36, 12 February 2022
Introduction
This, currently, is a minimum example of testing
Environment
Had a ton of problems trying to get this to work. Using the default for a project did not work. The test below using Retrofit and RxJava3.
implementation 'io.reactivex.rxjava3:rxandroid:3.0.0'
implementation 'io.reactivex.rxjava3:rxjava:3.0.13'
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
implementation 'com.squareup.retrofit2:adapter-rxjava3:2.9.0'
testImplementation 'junit:junit:4.13.2'
testImplementation 'org.mockito:mockito-inline:4.3.1'
Code to test
So this is the example I was testing. Specifically the RxJava part.
private fun fetchCountries() {
loading.value = true
disposable.add(
countriesService.getCountries()
// Create new Thread
.subscribeOn(Schedulers.newThread())
// Provide Thread to received Response
.observeOn(AndroidSchedulers.mainThread())
// What to do with it
.subscribeWith(object: DisposableSingleObserver<List<Country>>() {
// RxJava Success
override fun onSuccess(value: List<Country>?) {
Timber.d("All Good")
countries.value = value
countryLoadError.value = false
loading.value = false
}
// RxJava error
override fun onError(e: Throwable) {
Timber.d("All bad")
countryLoadError.value = true
loading.value = false
}
})
)
}
The CountriesService is injected with Dagger2 and uses Retrofit to call an API call.
class CountriesService {
@Inject
lateinit var api: CountriesApi
init {
DaggerApiComponent.create().inject(this)
}
fun getCountries(): Single<List<Country>> = api.getCountries()
}
Unit Test
This uses the RxPlugins to mock the async functions
...
class ListViewModelTest {
@get:Rule
var rule = InstantTaskExecutorRule()
@Mock
lateinit var countriesService: CountriesService
@InjectMocks
var listViewModel = ListViewModel()
private var testSingle: Single<List<Country>>? = null
@Before
fun setup() {
MockitoAnnotations.openMocks(this)
}
@Test
fun getCountriesSuccess() {
val country = Country(countryName = "countryName", "capital", "url")
val countriesList = arrayListOf(country, country)
testSingle = Single.just(countriesList)
`when`(countriesService.getCountries()).thenReturn(testSingle)
listViewModel.refresh()
assertEquals(2, listViewModel.countries.value?.size)
assertEquals(false, listViewModel.countryLoadError.value)
assertEquals(false, listViewModel.loading.value)
}
@Test
fun getCountriesError() {
testSingle = Single.error(Throwable( ))
`when`(countriesService.getCountries()).thenReturn(testSingle)
listViewModel.refresh()
assertEquals(true, listViewModel.countryLoadError.value)
assertEquals(false, listViewModel.loading.value)
}
@Before
fun setUpClass() {
RxJavaPlugins.reset()
RxAndroidPlugins.reset()
val immediate = object : Scheduler() {
override fun scheduleDirect(run: Runnable, delay: Long, unit: TimeUnit): Disposable {
return super.scheduleDirect(run, 0, unit)
}
override fun createWorker(): Worker {
return ExecutorScheduler.ExecutorWorker({ it.run() }, true, true)
}
}
RxJavaPlugins.setInitIoSchedulerHandler { immediate }
RxJavaPlugins.setInitComputationSchedulerHandler { immediate }
RxJavaPlugins.setInitNewThreadSchedulerHandler { immediate }
RxJavaPlugins.setInitSingleSchedulerHandler { immediate }
RxAndroidPlugins.setInitMainThreadSchedulerHandler { immediate }
RxJavaPlugins.setIoSchedulerHandler { Schedulers.trampoline() }
RxJavaPlugins.setComputationSchedulerHandler { Schedulers.trampoline() }
RxJavaPlugins.setNewThreadSchedulerHandler { Schedulers.trampoline() }
RxAndroidPlugins.setInitMainThreadSchedulerHandler { Schedulers.trampoline() }
}
@After
fun tearDownClass() {
RxJavaPlugins.reset()
RxAndroidPlugins.reset()
}
}