What is Mockito in Android, lets know it better.



Introduction to Mocks in Testing in Android 

A Test Double that tracks which of its methods were called. Mocks pass or fail a test depending on whether their methods were called correctly. 

When you think about testing you usually want to check that you do some actions and then the ending state is correct. Ex. You call a method and make sure that a value is correct or you press a button and then see if some text changes. 
So, mocks have a possibility of being misused to test implementation instead of the state change. 

By testing whether the methods have been called, you might end up testing that a bunch of methods were called, but not ultimately that anything actually changed. 

// Mock test Example 

myFragmentMock.changeMyUI()
verify(myFragmentMock).updateText("changed")

But it's better to just write a version of the test that doesn't use mocks for 2 reason: 


  1. Mock test doesn't check actual state change (mock test will pass even if there are errors in test.)
  2. Mock test is implementation dependent. (for the mock test, if your code changes so that you no longer need to call update test, the mock test will fail, even if maybe some other way the text box gets updated.)


// No mock test Example 

myFragment.changeMyUI()
assertThat(myFragment.textView.text, `is`("changed") )

So basically we can have false positive and false failures when we are mocking test.

Example : Navigation for fragment integration test 
So checking if the correct navigation method was called with all the correct arguments is the perfect use case of mock. So we mock the navigation controller here. 


So we can usually using mock in 2 kinds of situations: 


  1. When the end state change (opening other fragment, i.e. usually outside the scope of our test code )
  2. Navigation controller API 

So how do we use Mock ?
-> Using the Mockito Library


Mockito 

A framework for making test doubles. 
Mockito makes more than just mocks. It can make other tests doubles like stubs and spies. 

Example of making a mock with Mockito.

val navController = mock(NavController::class.java)

verify(navController).navigate(
    TasksFragmentDirections.actionTasksFragmentToTaskDetailFragment("id1")
)

Here we mock the class, then call verify method to check that the method was called with specific parameters as well.


How to use Mockito library?

Add Mockito Library to app/build.gradle file

// Dependencies for Android instrumented unit tests// Mockito Library androidTestImplementation "org.mockito:mockito-core:$mockitoVersion"androidTestImplementation "com.linkedin.dexmaker:dexmaker-mockito:$dexMakerVersion"


Mockito-Core library consist of all packages of mockito
Dexmaker-Mockito library is required to use Mockito in Android project. Mockito need to generate classes in runtime. On Android, this is done using something called DEX bytecode. So this library here enables Mockito to generate objects during runtime in Android.


Summary

  • Mockito creates NavController mock 
  • Attach that mocked NavController to the fragment 
  • verify navigate() called with correct action and parameters 


@RunWith(AndroidJUnit4::class)
@MediumTest@ExperimentalCoroutinesApiclass TasksFragmentTest {

    private lateinit var repository: TasksRepository

    @Before    fun initRepository() {
        repository = FakeAndroidTestRepository()
        ServiceLocator.tasksRepository = repository    }

    @After    fun cleanupDb() = runBlockingTest {        ServiceLocator.resetRepository()
    }
    @Test    fun clickTask_navigateToDetailFragmentOne() = runBlockingTest {
        // GIVEN - On the tasks screen with two tasks        repository.saveTask(Task("TITLE1", "DESCRIPTION1", false, "id1"))
        repository.saveTask(Task("TITLE2", "DESCRIPTION2", true, "id2"))

        // GIVEN - On the home screen        val scenario = launchFragmentInContainer<TasksFragment>(Bundle(), R.style.AppTheme)

        val navController = mock(NavController::class.java)
        scenario.onFragment {            Navigation.setViewNavController(it.view!!, navController)
        }        // WHEN - Click on the first list item        /*        onView(withId(R.id.tasks_list))            .perform(RecyclerViewActions.actionOnItem<RecyclerView.ViewHolder>(                hasDescendant(withText("TITLE1")), click()))                         */        // THEN - Verify that we navigate to the first detail screen        // THEN - Verify that we navigate to the first detail screen        verify(navController).navigate(
            TasksFragmentDirections.actionTasksFragmentToTaskDetailFragment( "id1")
        )

    }
}








N.K. 







Comments

Popular Posts