Developing Unit tests for an Android application (JUnit)

NOVASOLUTIONS.TECHNOLOGY is engaged in the development, support and maintenance of iOS, Android, PWA mobile applications. We have extensive experience and expertise in publishing mobile applications in popular markets like Google Play, App Store, Amazon, AppGallery and others.
Development and support of all types of mobile applications:
Information and entertainment mobile applications
News apps, games, reference guides, online catalogs, weather apps, fitness and health apps, travel apps, educational apps, social networks and messengers, quizzes, blogs and podcasts, forums, aggregators
E-commerce mobile applications
Online stores, B2B apps, marketplaces, online exchanges, cashback services, exchanges, dropshipping platforms, loyalty programs, food and goods delivery, payment systems.
Business process management mobile applications
CRM systems, ERP systems, project management, sales team tools, financial management, production management, logistics and delivery management, HR management, data monitoring systems
Electronic services mobile applications
Classified ads platforms, online schools, online cinemas, electronic service platforms, cashback platforms, video hosting, thematic portals, online booking and scheduling platforms, online trading platforms

These are just some of the types of mobile applications we work with, and each of them may have its own specific features and functionality, tailored to the specific needs and goals of the client.

Showing 1 of 1 servicesAll 1735 services
Developing Unit tests for an Android application (JUnit)
Medium
~3-5 business days
FAQ
Our competencies:
Development stages
Latest works
  • image_mobile-applications_feedme_467_0.webp
    Development of a mobile application for FEEDME
    756
  • image_mobile-applications_xoomer_471_0.webp
    Development of a mobile application for XOOMER
    624
  • image_mobile-applications_rhl_428_0.webp
    Development of a mobile application for RHL
    1050
  • image_mobile-applications_zippy_411_0.webp
    Development of a mobile application for ZIPPY
    947
  • image_mobile-applications_affhome_429_0.webp
    Development of a mobile application for Affhome
    862
  • image_mobile-applications_flavors_409_0.webp
    Development of a mobile application for the FLAVORS company
    445

Unit Test Development for Android Application (JUnit)

Android project without unit tests — project where scary to touch Repository or ViewModel, because unclear what breaks. JUnit 5 + Mockito + coroutines give tools to cover all business logic: fast JVM tests without emulator, isolated, reproducible.

Tool Stack

Tool Purpose
JUnit 5 Test runner, assertions
Mockito / MockK Mocks and stubs for dependencies
Turbine Kotlin Flow testing
kotlinx-coroutines-test TestDispatcher, runTest
Robolectric Android-specific code without emulator

MockK preferable to Mockito for Kotlin code: correctly mocks object, companion object and suspend-functions without runBlocking hacks.

ViewModel Testing with Coroutines

Main difficulty — ViewModel works with coroutines on Dispatchers.Main, which doesn't exist in JVM tests. Solution — TestDispatcher:

@OptIn(ExperimentalCoroutinesApi::class)
class UserViewModelTest {
    private val testDispatcher = UnconfinedTestDispatcher()

    @Before
    fun setup() {
        Dispatchers.setMain(testDispatcher)
    }

    @After
    fun tearDown() {
        Dispatchers.resetMain()
    }

    @Test
    fun `loadUser emits success state`() = runTest {
        val mockRepo = mockk<UserRepository>()
        coEvery { mockRepo.getUser("1") } returns User(id = "1", name = "Test")
        val viewModel = UserViewModel(mockRepo)

        viewModel.loadUser("1")

        assertEquals(UiState.Success(User(id = "1", name = "Test")), viewModel.uiState.value)
    }
}

UnconfinedTestDispatcher executes coroutines immediately, StandardTestDispatcher — only on advanceUntilIdle(). For timing tests (debounce, delay) use advanceTimeBy(ms).

Testing Kotlin Flow via Turbine

@Test
fun `state flow emits loading then success`() = runTest {
    val mockRepo = mockk<UserRepository>()
    coEvery { mockRepo.getUser(any()) } coAnswers {
        delay(100)
        User(id = "1", name = "Test")
    }
    val viewModel = UserViewModel(mockRepo)

    viewModel.uiState.test {
        assertEquals(UiState.Loading, awaitItem())
        viewModel.loadUser("1")
        assertEquals(UiState.Success(User("1", "Test")), awaitItem())
        cancelAndIgnoreRemainingEvents()
    }
}

Turbine (app.cash.turbine) — most convenient way to check sequence of emissions from StateFlow/SharedFlow without callback mess.

Repository and UseCase

Repository test isolated from ViewModel. Mock DataSource (remote and local), check caching logic, DTO → Entity mapping, error handling:

@Test
fun `getUser returns cached data when network fails`() = runTest {
    coEvery { remoteDataSource.getUser(any()) } throws IOException("No network")
    coEvery { localDataSource.getUser("1") } returns UserEntity(id = "1", name = "Cached")

    val result = repository.getUser("1")

    assertTrue(result.isSuccess)
    assertEquals("Cached", result.getOrNull()?.name)
}

What Often Not Tested but Should Be

  • Mapper classes — seems trivial, but exactly where nullable fields are lost and date-format incorrectly handled
  • Extension functions — especially ones formatting strings, dates, numbers
  • Pagination logic in PagingSourcePagingSource.LoadResult can be tested directly via TestPagingSource

CI Integration

./gradlew test runs all unit tests without emulator. Coverage via JaCoCo: ./gradlew jacocoTestReport. In GitHub Actions — matrix of JDK versions (17 + 21). Publish results as Test Report artifact for PR review.

Timeframe: 3–5 days depending on project size and current architecture.