Mobile Development 25 min read

Mastering Android Unit Testing: Practical Tips, Tools, and Real-World Cases

This article shares practical experiences and best practices for Android unit testing, covering why testing matters, simple test structures, mocking with Mockito, dependency injection via Dagger2, using Robolectric, CI integration, common pitfalls, and a detailed checkout flow example.

21CTO
21CTO
21CTO
Mastering Android Unit Testing: Practical Tips, Tools, and Real-World Cases

Guest Introduction

Zou Yong (aka Xiao Chuang), a senior Android developer at Mogujie Pay, explains his background and passion for unit testing and TDD.

Why Write Unit Tests?

Unit tests improve code quality, catch bugs early, enhance design, and actually save time despite the perceived overhead of writing and maintaining them.

Learning to write tests and adapting project structure both require initial effort, similar to learning to drive a car.

Tests run faster than full app execution.

Early bug detection reduces debugging time.

Refactoring becomes safer and quicker.

Simple Introduction to Unit Tests

Android offers several testing options (JUnit, Instrumentation, Espresso, etc.). The focus here is on JUnit‑based unit tests that run on the JVM, which are fast and isolated from Android framework dependencies.

A unit test typically consists of three steps: setup: instantiate the class under test and set preconditions. exercise: invoke the method being tested. verify: assert that the result matches expectations.

Testing void methods requires verifying side effects, such as whether a dependent component was called with the correct parameters.

Mock Concept and Mockito

Mocks replace real objects in tests, allowing you to specify return values and verify interactions. Mockito is the chosen framework, though it cannot mock static or final members.

Using Mocks with Dependency Injection

Two approaches are discussed: a dedicated testing flavor with factories, or dependency injection (DI). The article adopts DI using Dagger2, which cleanly separates object creation from usage.

In Dagger2, Module s provide dependencies and Component s expose them to clients. A TestingModule can override production bindings to supply mock implementations.

Robolectric: Solving Android Unit‑Testing Pain Points

Running pure JUnit tests on the JVM cannot use Android classes because they throw RuntimeException("Stub"). Robolectric provides a simulated Android environment on the JVM, enabling fast tests that use Android APIs.

Performance comparison:

Instrumentation testing: dozens of seconds.

Robolectric: ~10 seconds.

Pure JUnit: a few seconds.

The stack used in the project is JUnit4 + Mockito + Dagger2 + Robolectric.

A Concrete Case Study

The article walks through testing a checkout flow:

Launch CheckoutActivity and verify loadCheckoutData() is called.

Test CheckoutModel.loadCheckoutData() for correct API invocation.

Mock API responses to verify event bus posting for success and failure.

Validate UI updates in onCheckoutDataLoaded() for both success and error cases.

All dependencies (model, API, bus) are injected and mocked in tests. Full code is available on GitHub.

What Should Be Tested?

All public methods of models, presenters/view‑models, APIs, utilities.

Business logic inside data classes beyond simple getters/setters.

Custom view functionality (e.g., data binding, simple clicks).

Key activity behavior (view existence, data display, error handling).

CI and Code Coverage

Jenkins runs the Gradle test task on each push, using JaCoCo for coverage. Compatibility between Gradle JaCoCo (v7.1) and Jenkins plugin (≤1.0.19) is crucial.

Pitfalls and Good Practices

Native libraries cannot be loaded in pure JUnit or Robolectric; wrap System.loadLibrary in try‑catch or mock the wrapper.

Avoid static methods, singletons, and global state; prefer DI.

Don’t duplicate tests—test shared validators separately from builders.

Maintain a common test library for helpers, rules, and utilities.

Extract pure‑Java utilities from Android SDK to speed up JVM tests.

Leverage JUnit Rule s for reusable setup/teardown and descriptive failure messages.

Use Android Studio live templates and code generation to accelerate test writing.

Accept imperfect tests early; iterate and improve over time.

Consider BDD frameworks (Spock, Spek) for future enhancements.

Q&A Highlights

Answers cover UI interaction testing with Robolectric, handling legacy code, testing custom views, mocking network conditions, naming conventions for test methods, and strategies for testing native‑library dependent code.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Androidunit testingCIMockitoTDDRobolectricDagger2
21CTO
Written by

21CTO

21CTO (21CTO.com) offers developers community, training, and services, making it your go‑to learning and service platform.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.