Android Instrumentation Test with Espresso UI, Koin and Room
Testing, testing, and testing, these words every developer has heard and created the fear behind that. As a developer most of the time we don’t write a unit test, it may be the reason that it required more time to write and execute those. Somehow I agree with this point, and I am okay if the application works very smooth and scalable.
But if the application does not work smoothly or every time bugs generated then people will complain to the developer for application is not tested properly. It doesn’t matter how much time you invest in design and implementation, but mistakes are inevitable, and bugs will appear in the business logic.
Why testing is necessity?
There are many benefits of testing like it gives surety that our application feature working as per implementation and requirement. It also ensures that if we are going to implement any new feature in the application, which will not break my exiting feature etc. Okay, now we got to know there are many more benefits of testing.
As an android developer, we should know what are the testing available to test the feature. Generally, there are two types of testing we can do which are the unit test and instrument test.
Unit Test:
A unit test can be write in app/src/test/java
folder to our project, which runs by java JVM. Generally, it does not require any specific android library. The unit test covers the all business logic which we have written for our application feature. It mostly 70% of our codebase to take care.
Integration/InstrumentationTest:
Integration test can be written inside app/src/androidTest/java
folder. Android system will execute this test, it means to run this test required the android virtual device called emulator or real device. Generally, it can cover 20% of the codebase of our project.
I am not going to cover the unit testing which means the business logic test in this tutorial. We will see the instrumentation test which is based on the android instrument to execute the test cases.
First of all, we need to add few dependencies into the Gradle file to ensure that that can be perform. Here are the few lists.
// Core library implementation 'androidx.test:core:1.2.0' testImplementation "junit:junit:4.13-beta-3" // AndroidJUnitRunner and JUnit Rules implementation 'androidx.test:runner:1.2.0' implementation 'androidx.test:rules:1.2.0' // Assertions androidTestImplementation 'androidx.test.ext:junit:1.1.1' //espresso androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' androidTestImplementation 'androidx.test.espresso:espresso-contrib:3.2.0' // koin implementation 'org.koin:koin-test:1.0.2' // mock implementation 'io.mockk:mockk:1.9.2'
To execute the android instrumentation test file, the system required AndroidTestRunner. I would be recommended to create the custom TestRunner and FakeApplication file which needs to configure with Gradle file.
class FakeRunner : AndroidJUnitRunner() { override fun newApplication(cl: ClassLoader?, className: String?, context: Context?): Application { return super.newApplication(cl, FakeApplication::class.java.name, context) } }
class FakeApplication : Application() { override fun onCreate() { super.onCreate() } }
defaultConfig { //.... testInstrumentationRunner "com.sunil.arch.common.FakeRunner" }
Okay, now your TestRunner is ready to test the file. Let’s see an example to start testing our room database feature to make sure that working as per our expectation. Now we need to instantiate the Koin module which is required to create the Local database.
private const val DATABASE = "DATABASE" @RunWith(AndroidJUnit4::class) abstract class BaseTest: KoinTest { protected val database: AppDatabase by inject() @Before open fun setUp() { this.configureDi() } @After open fun tearDown() { StandAloneContext.stopKoin() } // CONFIGURATION private fun configureDi() { StandAloneContext.startKoin(listOf(configureLocalModuleTest(ApplicationProvider.getApplicationContext<Context>()))) } private fun configureLocalModuleTest(context: Context) = module { single(DATABASE) { Room.inMemoryDatabaseBuilder(context, AppDatabase::class.java) .allowMainThreadQueries() .build() } factory { (get(DATABASE) as AppDatabase).movieDao() } } }
Now my database module is configured with Koin and now we can apply the database testing. I am going to take an example of inserting fake data into our database table and validate to fetch the same data is inserted corrected or not. It this test passed then we can ensure that database inserting and loading feature is working correctly.
class MovieDaoTest : BaseTest() { override fun setUp() { super.setUp() initDataBase() } @Test fun getMovieData() { runBlocking { val movie = database.movieDao().getTopRatedMovies() Assert.assertEquals(1, movie.size) } } private fun initDataBase() { runBlocking { database.movieDao().save(DataSet.FAKE_MOVIE) } } }
Ok, we need to run this test file by AndroidTestRunner to check wether test case is passed or not. We need to run the below command while connected with the android device or android emulator.
gradlew connectedAndroidTest
It will start executing all the text files associated with androidTest folder. The test report will be generated inside your project directory report folder.
Now let’s see the Espresso UI test. Espresso is a testing framework contained in the Android Testing Support Library. It provides APIs to simulate user interactions and write functional UI tests.
Espresso tests are composed of three major components which are:
- ViewMatchers: These are a collection of objects used to find the desired view in the current view hierarchy. They are passed to the method to locate and return the desired UI element.
- ViewActions: They are used to perform actions such as views. They are passed to theÂ
ViewInteraction.perform()
 method. - ViewAssertions: They are used to assert the state of the currently selected view. They can be passed to theÂ
ViewInteraction.check()
 method.
In this tutorial, let’s see how can check the ViewMatcher component to ensure that the data is inflating on the correct view of our XML. Please disable all the animation of UI from the Gradle file to interact with testing.
testOptions { animationsDisabled = true unitTests.returnDefaultValues = true }
While writing the test classes for espresso we need to understand the few annotations which are commonly used in the android system which are described below:
The first thing to notice here are the annotations:
@RunWith(AndroidJUnit4.class)
: Tags the class as an Android JUnit4 class. Espresso unit test should be written as a JUnit 4 test class.@LargeTest
: Qualifies a test to run in >2s execution time, makes use of all platform resources, including external communications. Other test qualifiers areÂ@SmallTest
 andÂ@MediumTest
.@Rule
:ÂActivityTestRule
 andÂServiceTestRule
are JUnit rules part of the Android Testing Support Library and they provide more flexibility and reduce the boilerplate code required in tests. These rules provide functional testing of a single activity or service respectively.@Test
: Each functionality to be tested must be annotated with this. The activity under test will be launched before each of the test annotated withÂ@Test
.
Ok, let’s create the first espresso test Class to check the data is inflated on the correct view.
@RunWith(AndroidJUnit4::class) @LargeTest class DetailUIUnitTest { private lateinit var detailViewModel: DetailViewModel @get:Rule var mActivityRule: ActivityTestRule<MainActivity> = ActivityTestRule(MainActivity::class.java, true, false) @Before fun beforeTest() { mActivityRule.launchActivity(Intent()) // breakpoint here detailViewModel = mockk() } @After fun afterTest() { } @Test fun test_data_inflating_on_correct_view() { val movie = DataSet.FAKE_MOVIE.first() launchFragment(movie) Espresso.onView(ViewMatchers.withId(R.id.movie_name)).check( ViewAssertions.matches( ViewMatchers.withText( CoreMatchers.containsString(movie.title) ) ) ) }
You can run this file to check whether the test is failed or passed.
09/19 17:04:29: Launching 'DetailUIUnitTest' on samsung SM-J710F. Running tests $ adb shell am instrument -w -r -e debug false -e class 'com.sunil.arch.ui.DetailUIUnitTest' com.sunil.arch.test/com.sunil.arch.common.FakeRunner Waiting for process to come online... Connected to process 22556 on device 'samsung-sm_j710f-52037884fe916345'. Started running tests
WrappingÂ
Now we have a good understanding of android instrumentation testing. We can play with a few examples of instrumentation testing. You can get the full Github source code of Jetpack Sample. In my next tutorial, we will come with new technical stuff to discuss, ok till then enjoy your healthy day.
If you are wondering to learn Android then Please learn from Android category and wondering to learn Kotlin then Kotlin Category will help you. If you want to learn all the python article, then learn from the python category.
Happy Coding 🙂
I am a very enthusiastic Android developer to build solid Android apps. I have a keen interest in developing for Android and have published apps to the Google Play Store. I always open to learning new technologies. For any help drop us a line anytime at contact@mobologicplus.com
Hi!
Is it possible for you to update this article with the most recent version of Koin? (as of this writing v. 2.0.1)
Thanks for pointing, Once I will be free, I will be update this article.