Android App From Scratch Part 4— Creating Unit Tests with JUnit

Faruk Toptaş
Android Bits
Published in
4 min readMar 3, 2017

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 with Espresso
  6. Continuous Integration with Travis-CI

In this part I will create some unit tests. I mentioned that MVP is a good approach for unit testing. All logic is kept in presenters. Testing the presenter will cover almost the whole logic.

I used mockito-kotlin to mock objects.

testImplementation "com.nhaarman:mockito-kotlin:1.6.0"

MainPresenterTest.kt

MainPresenter class doesn’t have too much logic. It has only a method to create the ViewPager. So the test will only check that if MainContract.View method(s) are called or not.

At firs I mocked the data layer for the presenter. I created MockMainRepository that returns mock data.

class MockMainRepository : MainRepository {

override fun parseFeeds(): List<Feed> {
return MockData.FEEDS
}
}

Then inject repository into the Presenter.

class MainPresenterTest {

private lateinit var mainPresenter: MainContract.Presenter
private val view: MainContract.View = mock()

@Before
fun setup() {
val repository = MockMainRepository()
mainPresenter = MainPresenter(repository)
mainPresenter.attach(view)
}

@Test
fun testLoadItems() {
mainPresenter.loadRssFragments()

argumentCaptor<List<Feed>>().apply {
verify(view).onLoadRssFragments(capture())
Assert.assertEquals(firstValue, MockData.FEEDS)
}
}
}

setup() method runs before test methods. Initialisations done here.

mock() method creates a mock object. MainContract.View is mocked here.

In testLoadItems() method, presenter method loadRssFragments() is called then verifies that View callback is called or not with argumentCaptor(). capture() method captures the parameter passed to the method. firstValue is the value of captured object. With assertEquals method it is verified that the value is correct or not.

Now let’s run the test and see the result:

RssPresenterTest.kt

class RssPresenterTest {

private lateinit var presenter: RssContract.Presenter
private val cache = RssCache()
private val view: RssContract.View = mock()
private var repository = MockRssRepository(true)

@Before
fun setup() {
presenter = RssPresenter(repository, cache)
presenter.attach(view)
}

@Test
fun testLoadRssItems() {
presenter.loadRssItems(MockData.FEEDS[0], false)

argumentCaptor<List<RssItem>>().apply {
verify(view).showLoading()
verify(view).onRssItemsLoaded(capture())
Assert.assertTrue(firstValue == MockData.ITEMS)
verify(view).hideLoading()

}
}

@Test
fun testLoadRssItemsFromCache() {
cache.addContent(MockData.FEEDS[0].url, MockData.ITEMS)
presenter = RssPresenter(repository, cache)
presenter.attach(view)
presenter.loadRssItems(MockData.FEEDS[0], true)

argumentCaptor<List<RssItem>>().apply {
verify(view).onRssItemsLoaded(capture())
Assert.assertTrue(firstValue == MockData.ITEMS)
}
}

@Test
fun testFailRssItems() {
presenter = RssPresenter(MockRssRepository(false), cache)
presenter.attach(view)
presenter.loadRssItems(MockData.FEEDS[0], true)

argumentCaptor<Error>().apply {
verify(view).onFail(capture())
Assert.assertTrue(firstValue == Error.generic())
verify(view).hideLoading()
}
}

}

There are 3 test cases for RssPresenter :

  • Loading RSS Items by fetching service
  • Loading RSS Items from cache
  • Failing the RSS service

setup() method generates mock RSS items then attaches the presenter.

argumentCaptor methods are used to capture the method parameters of mocked View callbacks.

testLoadRssItems() is explained step by step:

  • Presenter onSuccess() method is called with mocked repository data
  • Checks if showLoading() method is called because data has to be fetched from a network call
  • Checks if onRssItemsLoaded() method is called with a successful response
  • Checks if captured object equals the mock objects
  • Checks if hideLoading() method is called after network call

testLoadRssItemsFromCache() methods calls presenter method with fromCache parameter enabled. Then checks if cached objects are returned.

testLoadFail() method calls presenter onFail() method with mock Url then checks if view’s onFail() is called properly.

Creating a TestSuite

To organize the execution of your unit tests, multiple unit test classes can be grouped with a TestSuite.

@RunWith(Suite::class)
@Suite.SuiteClasses(MainPresenterTest::class, RssPresenterTest::class)
class UnitTestSuite

This test suite will run all unit tests.

UnitTestSuite results

To see the test outputs from command line, Add the task below to app level gradle(not in android { … } part) :

tasks.withType(Test) {
testLogging {
events "started", "passed", "skipped", "failed"
}
}
Test outputs

Here is the full source code for this part:

Continue reading with the next part?

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

--

--