# Unit Testing with Mockito ###### tags: `Android Unit Testing` `Mockito` [TOC] # What are unit testing? 1. Automated 1. Only test business logic >**Note: >There should not be android specific code/views in these tests** [color=red] # Where to locate unit test in Android? ![](https://i.imgur.com/xQlZME3.png =250x250) * androidTest is an interfaceTest * test is unit test (Only logic testing) # Gradle.Build (App) setup ```kotlin= testImplementation 'android.arch.core:core-testing:1.1.1' ``` # Implementation ## New Test New a file class in **test** folder(**NOT** androidTest). ## Different Annotations We will have ### **@get:Rule** This one describes rules that the test will follow. In this case, we used **InstantTaskExecutorRule()**, this one will have error if Gradle.Build(App) is not set up with testImplementation. ```kotlin= @get:Rule var rule = InstantTaskExecutorRule() ``` **What is InstantTaskExecutorRule?** InstantTaskExecutorRule swaps the background executor used by the Architecture Components with a different one which executes each task synchronously. So when you need it especially when writing a unit test, you need to implement testImplementation dependency. ### **@Before** Those with this annotation, means that they will have to be run before the test. In this case mainThread needs to be dealed, as it is related to main Thread(UI Thread). ```kotlin= @Before fun setUpRxSchedulers(){ //When Observable is called, we want to return immediately (0 delay) val immediate = object : Scheduler() { override fun scheduleDirect(run: Runnable?, delay: Long, unit: TimeUnit?): Disposable { //0 for delay, as we do not want to wait when calling schedulers return super.scheduleDirect(run, 0, unit) } override fun createWorker(): Worker { return ExecutorScheduler.ExecutorWorker(Executor { it.run() }) } } RxJavaPlugins.setInitIoSchedulerHandler { immediate } RxJavaPlugins.setInitComputationSchedulerHandler { immediate } RxJavaPlugins.setInitNewThreadSchedulerHandler { immediate } RxJavaPlugins.setInitSingleSchedulerHandler { immediate } RxAndroidPlugins.setInitMainThreadSchedulerHandler { immediate } } ``` ### **@Mock** && **@InjectMocks** **Introduction to Mock:** A mock object might assert the order in which its methods are called, or **assert consistency of data** across method calls. **How to use?** **@Mock** We have to first deal whose variables that we will be using and annotating them with **@Mock** to assert that this variable is present and injected. For those that we will be creating them, such as dummy data, we do not annotate with @Mock. **@InjectMocks** But also we need to know where to use those mocks, so we will have **@InjectMocks** to specify where they will be put. **MockitoAnnotations.initMocks** We need to rememeber to initiate the mocks before the tests in order to use them. In this case countryService is used in ListViewModel Class. ```kotlin= @Mock lateinit var countryService : CountryService @InjectMocks var listViewModel = ListViewModel() private var testSingle : Single<ArrayList<Country>>? = null @Before fun setUp(){ MockitoAnnotations.initMocks(this) } ``` **Note:** Without @Mock annotation for injected variables/lateinit var, we will face errors like this below. ![](https://i.imgur.com/aSdgOgy.png) ### **@Test** **Single.just(Result)** Can Either be a real result or Throwable to test fail api call. **Mockito.when (\`when`)** When(Do this).thenReturn(the result you want) **Assert.assertEquals** This will be used to check the finishing value result for each of the variables. If the result does not fit the expected one, test will fail. ```kotlin= @Test fun getCountriesSuccess(){ val country : Country = Country("countryName", "capital", "url") val countryList : ArrayList<Country> = arrayListOf(country) //Can test success Api: It tell us it returns countryList testSingle = Single.just(countryList) //Mockito.when `when`(countryService.getCountries()).thenReturn(testSingle) listViewModel.refresh() //Write all expected results, if the outcome is different then test will fail Assert.assertEquals(1, listViewModel.countryList.value?.size) Assert.assertEquals(false, listViewModel.loadError.value) Assert.assertEquals(false, listViewModel.loading.value) //Right click on method and run test to check the result } @Test fun getCountriesFail(){ //Can test error Api testSingle = Single.error(Throwable()) //Mockito.when `when`(countryService.getCountries()).thenReturn(testSingle) listViewModel.refresh() //Write all expected results, if the outcome is different then test will fail Assert.assertEquals(null, listViewModel.countryList.value?.size) Assert.assertEquals(true, listViewModel.loadError.value) Assert.assertEquals(false, listViewModel.loading.value) } ``` ## Complete Test ```kotlin= class ListViewModelTest { @get:Rule var rule = InstantTaskExecutorRule() @Mock lateinit var countryService : CountryService @InjectMocks var listViewModel = ListViewModel() private var testSingle : Single<ArrayList<Country>>? = null @Before fun setUp(){ MockitoAnnotations.initMocks(this) } @Test fun getCountriesSuccess(){ val country : Country = Country("countryName", "capital", "url") val countryList : ArrayList<Country> = arrayListOf(country) //Can test success Api: It tell us it returns countryList testSingle = Single.just(countryList) //Mockito.when `when`(countryService.getCountries()).thenReturn(testSingle) listViewModel.refresh() //Write all expected results, if the outcome is different then test will fail Assert.assertEquals(1, listViewModel.countryList.value?.size) Assert.assertEquals(false, listViewModel.loadError.value) Assert.assertEquals(false, listViewModel.loading.value) //Right click on method and run test to check the result } @Test fun getCountriesFail(){ //Can test error Api testSingle = Single.error(Throwable()) //Mockito.when `when`(countryService.getCountries()).thenReturn(testSingle) listViewModel.refresh() //Write all expected results, if the outcome is different then test will fail Assert.assertEquals(null, listViewModel.countryList.value?.size) Assert.assertEquals(true, listViewModel.loadError.value) Assert.assertEquals(false, listViewModel.loading.value) } @Before fun setUpRxSchedulers(){ val immediate = object : Scheduler() { override fun scheduleDirect(run: Runnable?, delay: Long, unit: TimeUnit?): Disposable { //0 for delay, as we do not want to wait when calling schedulers return super.scheduleDirect(run, 0, unit) } override fun createWorker(): Worker { return ExecutorScheduler.ExecutorWorker(Executor { it.run() }) } } RxJavaPlugins.setInitIoSchedulerHandler { immediate } RxJavaPlugins.setInitComputationSchedulerHandler { immediate } RxJavaPlugins.setInitNewThreadSchedulerHandler { immediate } RxJavaPlugins.setInitSingleSchedulerHandler { immediate } RxAndroidPlugins.setInitMainThreadSchedulerHandler { immediate } } } ``` ## Imports used ```kotlin=== import androidx.arch.core.executor.testing.InstantTaskExecutorRule import io.reactivex.Scheduler import io.reactivex.Single import io.reactivex.android.plugins.RxAndroidPlugins import io.reactivex.disposables.Disposable import io.reactivex.internal.schedulers.ExecutorScheduler import io.reactivex.plugins.RxJavaPlugins import org.junit.Assert import org.junit.Before import org.junit.Rule import org.junit.Test import org.mockito.InjectMocks import org.mockito.Mock import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations import udemy.kotlin.countries.model.Country import udemy.kotlin.countries.model.CountryService import udemy.kotlin.countries.viewmodel.ListViewModel import java.util.concurrent.Executor import java.util.concurrent.TimeUnit ``` # Note: **AndroidSchedulers.mainThread()** **AndroidSchedulers.mainThread()** is not part of an Android System, but it is a way to access the main thread that will run on a device. But on tests, we do not want it.