Android App From Scratch Part 5 — Instrumentation Tests

Faruk Toptaş
Android Bits
Published in
3 min readSep 16, 2018

In this tutorial series, I will try to create an RSS Reader app step by step. Through this series I will explain:

  1. How to use Model-View-Presenter in an Android App
  2. Implementing must have libraries
  3. Implementing App Logic
  4. Creating unit tests with JUnit
  5. Creating Android Instrumentations tests
  6. Continuous Integration with Travis-CI

The cost of instrumentation tests is much more than JUnit tests. Because a device/emulator is needed to run instrumentation tests. If there is a network operation, UI tests get more complicated and slower. So try to use JUnit tests instead of instrumentation tests.

I will write instrumentation tests with Espresso and use mock responses to make UI tests faster. Espresso will not wait for the network operations. Also, I will test both success and error cases.

There’s a builtin espresso recorder tool in Android Studio. It works but generates spaghetti code. I use it once before writing tests. Then clean most of the boilerplate code.

Before reading the rest of this post, I highly recommend you to read Jake Wharton’s testing robots.

With the test cases I will verify that:

  • ViewPager has an adapter and fragments inside viewPager
  • RecyclerView has an adapter and item count is as expected
  • SwipeRefreshLayout is working and loading data
  • An error message is shown is fetching RSS fails

Test rule is set to prevent activity launch as soon as tests start.

@LargeTest
@RunWith(AndroidJUnit4::class)
class AllTests {

@get:Rule
var rule = ActivityTestRule(MainActivity::class.java,
false,
false)
}

Creating mock repositories with mock data.

class MockMainRepository : MainRepository {

override fun parseFeeds() = FEEDS

companion object {
val FEEDS = listOf(
Feed(0, "title1", MockRssRepository.SUCCESS_URL),
Feed(1, "title2", MockRssRepository.ERROR_URL)
)
}
}

For the first time fetchRss() method is called reponse contains 5 items. When this method called again it contains 10 items. This behaviour is for testing swipe refresh layout.

class MockRssRepository : RssRepository {

private var lastIndex = 1
private val count = 5

override fun fetchRss(url: String, listener: RssResponseListener) {
when (url) {
SUCCESS_URL -> listener.getResponse(url, RssResponse.success(ITEMS.subList(0, lastIndex++ * count)))
ERROR_URL -> listener.getResponse(url, RssResponse.error(Error.generic()))
}
}

companion object {
const val SUCCESS_URL = "https://github.com"
const val ERROR_URL = "error_url"

val ITEMS = listOf(
RssItem().apply {
title = "title0"
link = SUCCESS_URL
}, ....

)
}

}

I override NetworkModule to inject mock objects.

class MockNetworkModule : NetworkModule() {

@Provides
@Singleton
override fun provideRssRepository(service: RssService): RssRepository = MockRssRepository()

@Provides
@Singleton
override fun provideMainRepository(app: Application): MainRepository = MockMainRepository()
}

Before tests start mock dependencies are injected.

@Before
fun setup() {
NewsApp.setComponent(DaggerAppComponent.builder()
.appModule(AppModule(NewsApp.instance))
.networkModule(MockNetworkModule())
.build())

val intent = Intent()
rule.launchActivity(intent)
}

Now tests can be run.

@Test
fun loadViewPagerTest() {
news {
checkViewPagerItems
(rule)
swipeLeft()
swipeRight()
}
}

Here are the details of checkViewPagerItems()

fun checkViewPagerItems(rule: ActivityTestRule<MainActivity>) {
val viewPager = rule.activity.findViewById(R.id.viewPager) as ViewPager
Assert.assertNotNull(viewPager)
Assert.assertNotNull(viewPager.adapter)
Assert.assertTrue(MockMainRepository.FEEDS.size == viewPager.adapter?.count)
}

Other test cases:

@Test
fun recyclerViewItemsTest() {
news {
checkRecyclerViewItemCount(rule, 5)
checkRecyclerViewItemContent()
}
}

@Test
fun swipeRefreshTest() {
news {
checkRecyclerViewItemCount(rule, 5)
swipeDown()
checkSwipeRefresh(rule)
checkRecyclerViewItemCount(rule, 10)
}
}

@Test
fun loadFailed() {
news {
swipeLeft()
checkErrorTextView()
}
}

You can check my GitHub repo for the full source code:

If you liked the article, please 👏👏👏 so more people can see it! Also, you can follow me on Medium

--

--