How do I test an Android app? Start with fast local JVM tests for code that does not need the Android framework, then add instrumented tests on an emulator or physical device for Android integration and user-visible behavior. Use Compose testing APIs for Compose screens, Espresso for View-based screens, and add broader, slower checks only where they protect an important user path.
A local test and an instrumented test describe where code runs, not the entire testing strategy. A useful plan combines isolated logic checks, UI and integration coverage, adaptive-layout checks, and a small set of release-build tests.
As an Amazon Associate I earn from qualifying purchases.
What is the difference between local and instrumented tests?
A local test runs on your development machine or a CI server, normally on the JVM. It is the quickest choice for deterministic business logic that can run without Android framework classes. An instrumented test runs with an Android device or emulator, is packaged into a separate test APK, and can use an application context and instrumentation APIs.
Crashes, No Sound, or Screen Glitches?
Random freezes, missing sound and display glitches usually trace back to one bad driver. Find and replace yours safely.Free scan · under a minuteWindows Errors? Fix Them Before They Spread
Repair common Windows errors and clear accumulated junk for a smoother, more stable PC - no reinstall needed.Free scan · no reinstall| Choice | Best fit | What you gain | What it cannot prove by itself |
|---|---|---|---|
| Local JVM test | ViewModels or presenters, repositories, domain use cases, and utilities with dependencies replaced by fakes | Fast feedback and simple isolation | Full Android runtime, system UI, and device-specific behavior |
| Instrumented device or emulator test | Android framework integration, functional UI flows, and runtime behavior | Execution on Android with realistic lifecycle and platform services | Every screen size, posture, API level, or production configuration unless you add those cases |
| Robolectric JVM test | Selected Android-dependent tests where its simulation is adequate | Android-oriented execution without emulator startup | Complete fidelity for every API, system interaction, or visual detail |
| Screenshot test | Visual regression across components or window sizes | A rendered-image comparison against an approved baseline | Correctness of every interaction or backend state |
The environment should follow the behavior under test. Do not move a test to a device merely because it is called a unit test, and do not expect a JVM simulation to validate system UI that it does not implement.
#1 Best Overall
How do I choose a first Android testing plan?
Use a layered plan. Each layer answers a different question, so the fastest and most isolated check should own the behavior whenever possible.
1. Map the app’s behavior
List the decisions and state transitions a user depends on: loading and success states, validation, empty data, malformed responses, authentication failures, navigation, and state after recreation. Mark which code can run without Android classes. Those items are candidates for local tests.
2. Isolate external dependencies
Inject a repository, clock, dispatcher, network client, database gateway, or other boundary instead of constructing it inside the class being tested. Supply a fake or controlled test double. This keeps a local test independent of production services and makes failure cases reproducible.
The Tool Desk
Outbyte Driver Updater FREEScan for outdated or missing drivers - takes under a minuteDriver Scan →Outbyte PC Repair FREEClear out junk files and repair common Windows errorsFree Scan →3. Put Android-dependent behavior in the right layer
Activities, fragments, and services should not contain business rules that could be tested elsewhere. Test their wiring, lifecycle, and user-visible behavior with instrumented or UI tests. A local test should verify the ViewModel’s decision; an Android test should verify that the screen displays and responds to that decision.
4. Add a small number of high-value user paths
Cover navigation and flows that could crash during initialization, block sign-in, lose data, or make the main task impossible. Keep broad end-to-end checks proportional to their diagnostic value: they are slower and less isolated than local tests.
Where do Android tests live?
Android projects normally separate tests by source set:
module-name/src/test/java/contains local JVM tests.module-name/src/androidTest/java/contains instrumented tests for a device or emulator.
The same convention applies when your source is Kotlin; the directory name may be kotlin in a project that uses that layout. Android Studio can generate a test from a class or method. In the test-generation dialog, choose the destination source set: local tests go to test, while device tests go to androidTest.
Keep the test beside the module that owns the behavior. A data module can have local repository tests, an app module can have UI tests, and a shared domain module can remain mostly platform-independent. An instrumented test is compiled and packaged separately from the application, so it is not interchangeable with a local test simply by changing its folder.
How do I write my first Android unit test?
Begin with a class whose output can be determined from supplied inputs. A ViewModel, use case, parser, or repository decision is usually a better first target than an Activity’s framework lifecycle.
- Choose one behavior. For example, a use case should expose a welcome state when a fake repository returns a valid account.
- Define the boundary. Give the class a repository interface or another dependency that can be replaced in the test.
- Arrange controlled data. Return success, an empty result, malformed data, or a connection failure from the fake.
- Act once. Invoke the public method or send the event a real caller would send.
- Assert the result. Check the state or value the user-facing layer needs, not an internal helper call.
- Add boundary cases. Include invalid input, retries, cancellation or recreation-related state changes where they are part of the behavior.
A minimal Kotlin shape looks like this; adapt the names to your project and use the test and assertion libraries already present in it:
Rank #3
class WelcomeUseCaseTest {
@Test
fun validAccount_emitsWelcomeState() {
val repository = FakeAccountRepository(account = Account("Sam"))
val useCase = WelcomeUseCase(repository)
val result = useCase.load()
assertEquals(WelcomeState.Ready("Sam"), result)
}
}
The important properties are isolation and an observable outcome. The fake repository prevents a network service from deciding whether the test passes. Add separate tests for an empty account, malformed data, and a connection failure if the use case handles them differently.
Quick wins for a faster PC:
Scan for outdated or missing drivers - takes under a minuteDriver Scan →Clear out junk files and repair common Windows errorsFree Scan →When should a test run on an emulator or a physical device?
Choose an instrumented test when Android itself is part of the behavior you need to verify. Examples include lifecycle and context interactions, database integration on the Android runtime, functional UI flows, permissions, system navigation, and APIs that a JVM cannot faithfully provide.
Do I need an emulator to test an Android app?
Not for every test. Local JVM tests run without one. You do need either a physical Android device or an emulator for instrumented tests. A device gives you hardware and vendor behavior; an emulator makes repeatable API levels, screen sizes, and postures easier to provision. A practical beginner setup is to keep most logic tests local and maintain at least one instrumented target for framework and UI behavior.
Run an instrumented test
- Create or open a test under
module-name/src/androidTest/java/. - Connect an unlocked device with developer debugging enabled, or create and start an Android Virtual Device with the API level and screen configuration you support.
- Select the instrumented test or the module’s
androidTestconfiguration in Android Studio. - Run it and inspect the device log and test result. The test APK is installed alongside the application APK.
Use a device or emulator for behaviors that depend on unsupported or partially supported APIs, system UI, or fidelity that a local simulation cannot provide. WebView behavior and interactions with system UI are examples where a JVM substitute should not be treated as conclusive.
How do I test a Jetpack Compose app?
Compose tests use semantics rather than view IDs. A test can query a semantic node, perform an action, and assert the resulting state. The assertion should describe what the user can observe: after tapping “Continue,” “Welcome” is displayed, rather than merely asserting that a framework method was called.
Do these 3 things before closing this tab:
1Repair Windows errors before they cause bigger problems2Scan for outdated or missing drivers - takes under a minute3Clear out junk files and repair common Windows errorsRank #4
Set up the Compose test rule
The official setup pattern uses the Compose UI JUnit dependency with the version variable already defined by your project:
androidTestImplementation("androidx.compose.ui:ui-test-junit4:$compose_version")
The ui-test-manifest debug dependency is needed when you use createComposeRule(). It is not needed for createAndroidComposeRule<YourActivity>(). Do not copy a fixed version from an old article; keep the dependency aligned with the Compose versions selected for the target project.
Test composable content directly
- Create a Compose test rule.
- Set the content under test, or launch the activity with an Android Compose rule.
- Find a node by meaningful text, content description, or another semantic property.
- Perform the user action, such as a click or text entry.
- Assert the new semantic state or visible text.
@get:Rule
val composeRule = createComposeRule()
@Test
fun continue_showsWelcome() {
composeRule.setContent { WelcomeScreen() }
composeRule.onNodeWithText("Continue").performClick()
composeRule.onNodeWithText("Welcome").assertIsDisplayed()
}
Choose stable semantics that represent the interface, not implementation details that are likely to change during refactoring. For a screen that depends on navigation, activity resources, or real Android integration, use createAndroidComposeRule<YourActivity>() and keep that test in androidTest.
How do I test a View-based UI with Espresso?
For XML layouts and traditional Views, Espresso provides actions and assertions against displayed elements. A typical flow finds the “Continue” control, clicks it, and checks that “Welcome” is visible. Keep the test focused on the interaction and result; the fact that an Espresso API was invoked is not the behavior users care about.
Free tools Windows power users keep installed
One-click scans. No signup required.
Hybrid screens may contain both Compose and Views. Use the framework that owns each portion and verify the handoff at their boundary. For example, a View-hosted Compose container can be exercised with Compose semantics while the surrounding navigation is covered by the appropriate Android UI test.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.When is Robolectric a sensible choice?
Robolectric simulates selected Android classes on the JVM, allowing some Android-oriented and UI behavior tests to run without starting an emulator. It can be useful for legacy code or Android APIs that are difficult to isolate and where its supported behavior is sufficient.
Use it selectively rather than wrapping every local test in the simulator. Keep ordinary platform-independent tests free of Robolectric so they remain simple and fast. Keep a device or emulator test for unsupported or partially supported APIs, system UI, WebView, lifecycle behavior whose fidelity matters, and visual or hardware-sensitive cases. A passing Robolectric test proves the simulated behavior you exercised; it does not prove that every Android version and device will behave identically.
How much testing is enough?
Think in terms of scope and feedback cost. Android’s testing strategy spans unit and component checks through feature and application tests, then release-candidate tests. Each broader layer increases fidelity and execution time while reducing isolation.
Recommended Free Tools
| Scope | Typical question | Execution and build considerations |
|---|---|---|
| Unit or component | Does this rule, transformation, or component state behave correctly? | Usually local, isolated, and fast |
| Feature | Does a meaningful feature work across its collaborating pieces? | May require Android integration and a debuggable build |
| Application | Can a user complete a major flow through the app? | Broad UI and navigation coverage; commonly a debuggable build |
| Release candidate | Does the optimized artifact behave like the one you will ship? | Runs a minified, optimized release build in an environment close to production |
There is no universal percentage split or ideal test count. Spend effort where a failure would matter and where the chosen layer can diagnose it. A parser’s edge cases belong in local tests; a database query that depends on Android’s runtime deserves integration coverage; a critical onboarding path merits an instrumented UI check.
How do I test tablets, foldables, rotation, and resizing?
A phone-sized screenshot or one successful launch is not evidence that an adaptive interface works. Exercise compact, medium, and expanded window sizes, including resizable windows where your app supports them.
Check layout and interaction at each size
- Verify that content remains reachable and controls do not overlap.
- Check navigation changes, such as a list-detail arrangement at an expanded width.
- Use UI behavior tests for interactions and state changes.
- Use screenshot tests when a rendered visual comparison is the most efficient way to catch spacing, clipping, or responsive-layout regressions.
Check state restoration
Recreate the activity and test rotation, resizing, and relevant configuration changes. Confirm that navigation position, entered values, selected items, and other promised state survive recreation. A screen that merely redraws without restoring the user’s progress has not passed this check.
Check foldable postures deliberately
If layout logic reacts to folding or unfolding without a configuration change, use a device or emulator that supports the posture you need to exercise. A static screenshot at one size cannot validate hinge-aware behavior.
A practical troubleshooting checklist
- The local test needs an Activity or Context: move the decision into a platform-independent class, inject the boundary, or choose an instrumented test if Android behavior is genuinely required.
- The instrumented test cannot find the device: confirm that the emulator is fully booted or that the physical device is unlocked and debugging is authorized; then rerun the test configuration.
- A Compose node is not found: inspect the composable’s semantics, use a stable text or content description, and make sure the test is rendering the intended content or launching the intended activity.
- A UI test passes locally but fails on another size: repeat it at compact, medium, and expanded windows and inspect state restoration after resize or recreation.
- Robolectric and a device disagree: treat the device result as authoritative for unsupported APIs, system UI, WebView, and fidelity-sensitive behavior; keep a targeted instrumented regression test.
- A broad test is hard to diagnose: add a narrower local or component test for the failing rule, then retain the broad test for the user path it protects.
A decision framework for every new test
- Can the behavior run without Android? Write a local JVM test and replace external services with fakes.
- Does it depend on Android APIs but not on unsupported system behavior? Consider a focused Robolectric test, while retaining device coverage where fidelity matters.
- Does it involve lifecycle, framework integration, system UI, WebView, permissions, or a real user flow? Use an instrumented device or emulator test.
- Is the risk primarily visual or responsive? Add screenshot checks across the window sizes that matter, plus interaction checks for behavior.
- Could optimization change the result? Run the high-value flow against a minified release candidate, not only a debuggable build.
This decision keeps fast feedback close to the code while reserving device time for evidence that only Android can provide.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

