Android Testing: Difference between revisions
Jump to navigation
Jump to search
(One intermediate revision by the same user not shown) | |||
Line 18: | Line 18: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
=Code to test= | =Code to test= | ||
So this is the example I was testing. | So this is the example I was testing. Specifically the RxJava part. | ||
<syntaxhighlight lang="kotlin"> | <syntaxhighlight lang="kotlin"> | ||
private fun fetchCountries() { | private fun fetchCountries() { | ||
Line 59: | Line 59: | ||
fun getCountries(): Single<List<Country>> = api.getCountries() | fun getCountries(): Single<List<Country>> = api.getCountries() | ||
} | |||
</syntaxhighlight> | |||
=Unit Test= | |||
This uses the RxPlugins to mock the async functions | |||
<syntaxhighlight lang="kotlin"> | |||
... | |||
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() | |||
} | |||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> |
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()
}
}