Testing and previewing

What is mobile app testing? Unit tests, E2E tests, and devices

Mobile app testing checks an iOS or Android app at different scopes, from unit tests to end-to-end journeys, on simulators, emulators and real devices.

8 min read

Quick answer

Mobile app testing checks that an iOS or Android app behaves correctly on the devices and in the conditions its users have. Tests differ in scope, from a unit test of one function to an end-to-end (E2E) test of a full user journey, and in where they run: your computer, a simulator or emulator, or a physical phone. Record which build each result came from.

What is the difference between test scope and test environment?

The nightly E2E job fails with Timeout: "Save" not visible after 10000ms, while the pull request says every test passed locally. Both are true. The local run was a Jest component test with a mocked network response; the nightly run installed a release build on an Android emulator that talked to a real test server.

Scope is how much of the app a test exercises. Environment is where the test runs: a test runner on your machine, a simulator or emulator, or a real device. A simulator can host an E2E test or a small integration check, so "unit tests" and "simulator tests" are not two kinds of the same thing.

Android's testing fundamentals and React Native's testing overview both describe tests at several levels. Choose the mix from the failures you need to catch.

Test scopeQuestion it answersExample
UnitDoes a small piece of logic behave correctly?A price calculation handles an empty cart
ComponentDoes a UI unit respond correctly to its inputs?A form shows an error for an invalid value
IntegrationDo connected parts agree on their contract?A repository saves and reloads a record
End-to-end (E2E)Can the user complete a journey through the app?Sign in, create a record, and find it after reopening the app

The labels overlap in practice, so write down what each test includes. A component test with a mocked network response is useful, and it still cannot show that the production service returns that response.

Which tools test iOS, Android and React Native apps?

Pair a unit test runner with one E2E tool, then add a cloud device service when you need hardware you don't own. For React Native apps the unit layer is usually Jest with React Native Testing Library. Native iOS tests use Apple's XCTest, and native Android UI tests use Espresso. Appium, Detox and Maestro drive an installed app from the outside, while AWS Device Farm, BrowserStack App Automate and Firebase Test Lab run your tests on real devices in their data centers.

ToolWhat it isBest for
AppiumOpen-source UI automation for iOS, Android and other platforms, through platform driversOne automation API across many platforms
AWS Device FarmTesting service that runs Appium, Espresso and other frameworks on real devices hosted by AWSTeams on AWS that need many real phones and tablets
BrowserStack App AutomateReal iOS and Android devices in the cloud that run Appium, Espresso, XCUITest and Maestro testsRunning an existing suite across many device models
DetoxGray-box E2E testing framework for React Native apps, with built-in Jest supportReact Native teams that want E2E tests in JavaScript
EspressoAndroid's UI testing framework, which synchronizes with the app's UI automaticallyNative Android UI tests
Firebase Test LabReal and virtual devices in Google data centers for Android instrumentation, Robo and iOS XCTest runsExisting Firebase users with a plan to migrate before the shutdown
JestJavaScript test runner for unit, component and snapshot testsLogic and components in React Native apps
MaestroOpen-source E2E UI testing for iOS, Android and web, with flows written in YAMLReadable E2E flows that anyone on the team can review
React Native Testing LibraryUtilities for rendering and querying React Native components inside JestComponent tests that check what the user sees
XCTest and XCUITestApple's framework for unit, performance and UI tests in XcodeNative iOS unit and UI tests

Check Firebase Test Lab's status before you build on it. Google's Test Lab documentation marks the service as deprecated, with a full shutdown on September 30, 2027, and points users to Developer Device Platform on Google Cloud.

Simulator, emulator or real device: where should a test run?

A test runner on your machine gives fast feedback without installing an app. Simulators and emulators run the installed app in repeatable device configurations. Physical devices show hardware and operating conditions that virtual devices cannot fully reproduce.

EnvironmentGood useWhat it misses
Test runner on your machineBusiness logic and isolated UI behaviorThe real native runtime and operating system integration
iOS Simulator or Android EmulatorRepeatable app journeys and many lifecycle checksHardware fidelity and realistic performance
Physical test deviceSensors, device integrations and real performance checksThe full range of your users' devices and conditions
Production monitoringSeeing behavior in real user sessionsControlled reproduction and catching bugs before release

A simulator is not a physical phone, and streaming one to a browser doesn't change that. Pick real devices from your product requirements and the devices your users have. A camera permission prompt in a simulator tells you nothing about a camera workflow under real lighting or memory pressure.

Run the final checks on the release build. Debug configuration changes logging and error handling, and the production build can carry different native settings.

What should a mobile test actually check?

Opening a checkout screen is not completing a purchase, and seeing "Saved" doesn't prove the right record was stored. For anything that changes data, check the resulting state through a test API or a later user action.

Start every test from a known state, with a dedicated test account and controlled data. If each test depends on whatever the previous run left behind, its failures are hard to read.

A short Maestro flow can drive an installed app's interface:

This flow assumes a signed-in test account, those exact visible labels and no existing note with the same text. Replace the app ID and labels with your own; Maestro's command documentation covers the syntax. The flow proves one narrow visible result, so add a relaunch check or a backend assertion if the feature promises durable storage.

Keep test accounts and payment environments away from live customer activity. Give every test a cleanup path, and never rely on a real charge to show that a UI action works.

How do you test failures, interruptions and upgrades?

Phones lose signal. Apps move between foreground and background, users deny permissions, and people come back after their session has expired.

Choose failure states from the feature. A map needs a useful screen when location access is denied, a queued upload needs a visible pending state and a way to recover, and a sign-in flow has to handle the return from an external app or browser.

Test upgrades as well as clean installs. Existing users have data and older configuration, and a schema migration can fail while every clean-install test passes.

Treat accessibility as part of a working journey. Check focus order and that VoiceOver and TalkBack can identify every control, because a screenshot cannot show that a screen is operable.

Wait for the resulting state with a timeout instead of a fixed sleep, and keep the failure output. An arbitrary delay makes a test slower without making it more reliable. When a test fails intermittently, find the cause: a retry can absorb a slow emulator boot, and repeated retries can also hide a real race in the app.

How do test results feed a release decision?

Record which build ran, where it ran and what the test covered. A pass from an older build cannot approve a new one, so keep the source commit with the build and save logs from every failure.

Run cheap checks on every change and save device runs for the changes that need them. A native dependency upgrade deserves different checks from a copy fix.

A useful release summary names the journeys you tested and the gaps you know about. "All tests passed" means little when every test ran on one platform. Decide whether each known gap blocks the release or ships with a written reason; the team that owns the app makes that call.

Where Expo fits

Expo projects run unit and component tests with the jest-expo preset, which configures Jest and mocks the native part of the Expo SDK, alongside React Native Testing Library. Development builds give you an app-specific native runtime for checks that need your own native libraries.

EAS Workflows can chain a build job to a test job, so each test result points at one specific build. Its pre-packaged jobs include a maestro job that runs flows on an Android Emulator or iOS Simulator build; Maestro jobs are in alpha. The E2E testing guide runs this workflow on every pull request:

Expo Simulators is in early access with a waitlist. Check current access and platform support before you make it part of a test plan.

Limitations

Virtual devices can check many functional outcomes, but you still need physical-device checks for the hardware behavior and performance your product depends on. Test automation also needs maintained expectations: a generated test that repeats the implementation's mistake will pass and prove nothing.

Next step

Choose one critical journey and use the React Native testing overview to map each important assertion to the scope and environment that can actually prove it.

Verified on 12 September 2026.

Keep reading

Frequently Asked Questions