Mobile Development 23 min read

Master Android Unit Testing: Why It Matters and How to Do It

This article explains the importance of unit testing in Android development, outlines why developers should invest time in it, and provides a step‑by‑step guide on writing effective Android unit tests using JUnit, Mockito, Dagger2, and Robolectric, along with practical tips, CI integration, and common pitfalls.

Tencent TDS Service
Tencent TDS Service
Tencent TDS Service
Master Android Unit Testing: Why It Matters and How to Do It

Dev Club is a community for mobile developers to share knowledge and network. In this session, Android engineer Xiao Chuang from Mogujie shares "Android Unit Testing: What, Why and How".

1. Why Write Unit Tests?

Unit tests improve code quality, catch bugs early, and enhance design. Although they require learning and occasional project restructuring, they ultimately save time by reducing debugging and speeding up refactoring.

Key points:

Learning to write tests takes time.

Integrating tests into an existing project may need structural adjustments.

Just like learning to drive, the initial investment pays off in faster, safer development.

2. How to Do Unit Testing on Android

2.1 Difference Between Unit Tests and Other Tests

Android offers several testing frameworks: JUnit, Instrumentation tests, Espresso, UiAutomator, Appium, Robotium, Robolectric, etc. This talk focuses on JUnit with Robolectric, which runs tests on the JVM quickly, unlike integration tests that require an emulator or device.

Unit tests run on the JVM and can test small code units fast, while integration tests run on a full Android environment and are slower.

2.2 Definition of a Unit Test

A unit test verifies a single method by setting up the test object, executing the method, and asserting the result.

2.3 Testing Void Methods & Common Misconceptions

Testing a void method involves verifying that it calls the expected collaborators. For example, DataActivity.loadData() should invoke DataModel.loadDataFromNetwork(). This is verified by mocking DataModel and checking the interaction, not by running the full UI flow.

2.4 Mocking with Mockito

Mockito creates fake objects to replace real dependencies, allowing you to specify return values and verify method calls. It cannot mock static or final methods, which requires alternative approaches.

2.5 Dependency Injection

DI separates object creation from usage. Using Dagger2, modules provide dependencies (e.g., DataModel) and components inject them into clients (e.g., DataActivity). In tests, a TestingModule overrides production modules to supply mock implementations without altering production code.

2.6 Robolectric

Robolectric simulates Android classes on the JVM, enabling fast unit tests that involve Android APIs. It is slower than pure JUnit but much faster than instrumentation tests.

3. Case Study: Checkout Flow

The presenter demonstrates a real‑world example: testing CheckoutActivity and CheckoutModel. Tests verify that the activity launches, the model calls the API with correct parameters, the API callback triggers the correct events, and the UI updates for success and error cases. All dependencies (model, API, event bus) are mocked using Dagger2.

4. Additional Topics

4.1 What to Test

All public methods of models, presenters, APIs, and utilities.

Logic in data classes beyond auto‑generated getters/setters.

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

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

4.2 CI and Code Coverage

Integrate tests into Jenkins; run Gradle test tasks on each push and collect coverage with JaCoCo.

4.3 Testing Private Methods

Change private methods to package‑protected or protected and place test classes in the same package.

4.4 Common Pitfalls & Best Practices

Native libraries are not supported by pure JUnit or Robolectric; wrap them or use shadows.

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

Create a shared test library for common helpers and rules.

Copy pure‑Java Android utilities (e.g., TextUtils) to reduce Android dependencies.

Leverage JUnit Rules for reusable setup/teardown logic.

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

Don’t strive for perfect tests initially; incremental coverage is valuable.

Martin Fowler: "Imperfect tests, run frequently, are much better than perfect tests that are never written at all."

4.5 Future Directions

Exploring BDD with Groovy/RoboSpock or Kotlin/Spek, though not yet adopted.

5. Conclusion

The talk covered unit testing fundamentals, JUnit, Mockito, DI with Dagger2, Robolectric, CI integration, and practical advice for writing maintainable Android tests. For detailed code examples, see the GitHub repository linked in the Q&A.

Androidunit testingmockingCIMockitoRobolectricDagger2
Tencent TDS Service
Written by

Tencent TDS Service

TDS Service offers client and web front‑end developers and operators an intelligent low‑code platform, cross‑platform development framework, universal release platform, runtime container engine, monitoring and analysis platform, and a security‑privacy compliance suite.

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.