Key takeaways
- Mocking APIs is right move for unit tests and integration tests. For E2E mobile tests, it often hides bugs you're trying to catch.
- The biggest risk of API mocking is drift. Your mock returns one shape, production returns another, and your tests pass while app crashes.
- Mobile mocking has problems web mocking doesn't: certificate pinning, network state transitions, auth token refresh, and offline-first sync. Apply wrong pattern at wrong layer and test gives false confidence.
Mocking APIs in mobile tests means replacing real network calls with simulated responses so tests run fast, deterministic, and offline. It's right pattern for unit and integration tests. It's often wrong pattern for E2E tests, where it strips away very thing you're testing.
The question of mocking APIs is one corner of broader mock vs real debate. The bigger question is which dependencies to fake. This post focuses specifically on APIs and mobile-specific traps that come with them.
What API mocking actually does
Mocking an API replaces network call with a hardcoded response. The app code doesn't know it's talking to a mock. The mock returns same JSON shape, same status codes, same headers as real backend would.
Three common patterns:
- In-process mocks. Interceptors that catch call before it hits network. OkHttp interceptors on Android, URLProtocol on iOS, MSW for React Native web layers.
- Local mock servers. A small HTTP server (WireMock, MockServer, Postman mock) running on localhost or in CI. The app calls a different base URL.
- Service virtualization. A full stand-in for a complex API with stateful behavior. Hoverfly, Mountebank.
For mobile, in-process mocks dominate at unit and integration layers. Local mock servers dominate at E2E layer when teams choose to mock at all.
When mocking APIs is right move
Mock when one or more of these hold:
The backend isn't ready. Front-end builds against a contract. Backend ships implementation. Mocks let both teams work in parallel against same OpenAPI spec.
The endpoint is slow or rate-limited. Third-party APIs that cost money per call, rate-limit aggressively, or take seconds to respond. Mock them locally and run thousands of test iterations.
You need to trigger error paths. Production APIs almost never return 503. Mocking lets you test how app handles timeout, 429, malformed JSON, or partial responses.
The data is non-deterministic. Live endpoints return different data each call. Tests that assert on real data flake. Mock response, lock assertion.
You're testing a unit. Pure logic that calls an API client doesn't need network. Mock client, assert on call and response handling.
According to Martin Fowler's "Mocks Aren't Stubs", canonical piece on topic, mocks should be applied at boundaries of your system, not in middle of it. For mobile, that means network layer is a fair boundary. Mocking screen above it usually isn't.
When mocking APIs is wrong move
This is where most posts stop. The harder question is when not to mock.
E2E tests on real devices. If test exists to prove user flow works end-to-end, mocking API removes entire backend from test. You're now testing UI logic against a contract, not real system.
Tests for auth and session handling. Token refresh, OAuth redirects, biometric step-up, certificate pinning. These need real auth flows or your test passes while production breaks.
Tests for data shape regressions. A mock returns what you told it to return. If backend silently changes a field from string to string | null, real app crashes, mocked test still passes.
Performance and load behavior. Connection pooling, TLS handshake, retry policy, payload size. None of these exist in a mock.
Sync, conflict resolution, and offline-first logic. If your app does optimistic writes and reconciles with server, mocking server skips half logic.
For end-to-end coverage, mocking apis is sometimes wrong move, see when to hit real endpoints instead. The rule is simple: if test exists to catch integration failures, integration has to be real.
How mocking fits mobile test pyramid
Different test layers want different mocking strategies.
The most common mistake is one of two extremes. Either teams mock everything (fast tests, false confidence) or mock nothing (slow tests, flaky CI). The pyramid above is workable middle.
Mobile-specific mocking traps
Mobile mocking has constraints web mocking doesn't.
Certificate pinning blocks your mock server. If app pins its TLS certificate to production CA, your local mock server with a self-signed cert fails handshake. Workaround: disable pinning in debug builds, or install mock server's cert in device trust store. Both have security implications.
iOS App Transport Security blocks HTTP. ATS requires HTTPS. A local mock on http://localhost:8080 gets blocked unless you add an NSAppTransportSecurity exception for debug build.
Network interceptors get bypassed by native SDKs. OkHttp interceptors don't see calls made by Firebase SDK or a vendored C library. If you mock at OkHttp layer, those calls go to production.
Real device tests can't reach localhost. A mock running on CI runner is on a different network from a real device cloud. You either expose mock publicly (security risk) or run mock on same network as device.
Token refresh races. When app's auth token expires mid-test, SDK refreshes silently. If your mock doesn't handle refresh endpoint, test fails in a way that looks like a UI bug.
These aren't reasons to skip mocking. They're reasons to know what layer you're mocking at and what mock actually covers.
A practical mocking setup for mobile
For a typical mobile team, this layered approach works:
Unit tests: Mock API client interface directly in code. No network, no server. Fast.
val mockApi = mockk<UserApi>()
every { mockApi.getUser("123") } returns User("123", "Jane")
val viewModel = UserViewModel(mockApi)β
Integration tests: Run real HTTP layer (OkHttp / URLSession) against a local mock server (WireMock, MockServer). Verify request shape, header handling, retry logic.
wireMockServer.stubFor(
get(urlEqualTo("/users/123"))
.willReturn(okJson("""{"id":"123","name":"Jane"}"""))
)β
E2E happy path: Run against a real staging backend. Real auth, real data, real network. The slowest tests, but most valuable.
E2E error paths: A man-in-the-middle proxy (Charles, mitmproxy) or a controllable mock layer that app points to in test mode. Trigger 500s, timeouts, slow connections.
The split matters. Unit and integration tier mocks are cheap to maintain. E2E mocks tend to drift and lie. Run happy path against real backends and you'll catch contract drift mocks miss.
The drift problem and how to manage it
Mocks lie when real API changes and mock doesn't. The longer your test suite passes against stale mocks, more dangerous they get.
Three patterns for managing drift:
Contract testing. Tools like Pact verify that mock matches what real backend actually returns. The mock is generated from spec, spec is verified against live service.
Recorded mocks. Tools that record real API responses once, then replay them. WireMock and Charles support this. Re-record periodically to catch drift.
Mock health checks in CI. Run a small E2E suite against real staging backend nightly. If it fails while mocked suite passes, mocks have drifted.
Flaky network responses are a top cause of test instability, which is why mocking apis matters at lower test tiers. But same mocks at E2E tier produce a different flakiness: tests that pass locally and fail in production because mock didn't match reality.
Mock APIs vs real APIs in E2E mobile tests
E2E mobile testing is where mocking question gets contentious. Two camps:
Mock everything to make tests deterministic. Sets up tests that pass reliably, run fast, work offline. Misses contract drift, auth issues, network state bugs. Common in selector-based test frameworks that struggle with timing flakiness.
Hit real staging APIs. Slower, occasionally flaky, but catches integration failures that matter. Requires a stable staging environment and proper test data management.
The right answer is layered. Run happy paths against real APIs. Mock only failure paths and third-party services you can't reliably trigger.
For teams running E2E tests on real devices, test framework matters here. Drizz tests written as plain-English commands (Tap "Submit", Validate "Order placed" is visible) don't have timing flakiness that pushes teams to mock everything just to keep CI green. Adaptive wait logic handles real network latency, so test layer doesn't need to fake network just to make tests reliable.
That changes cost-benefit math on mocking. If your real-API E2E suite has a 60% pass rate from flakiness, mocking starts to look attractive even though it hides bugs. If real-API E2E suite has a 97% pass rate, you can afford to keep network real and mock only parts you genuinely can't trigger.
Common mistakes
Mocking your own code. Mock at boundaries (HTTP layer, third-party SDKs), not inside your own modules. Mocking your own classes locks tests to implementation details.
Hardcoding response data forever. Mocks should look like real production data. Use realistic payloads, edge cases, and error shapes. A mock that returns {"data": "ok"} doesn't test parser.
Forgetting to test error path. Half of mocking's value is triggering errors. Teams set up mocks for happy path and never write failure tests.
No cleanup between tests. Stateful mocks that leak between tests cause failures that look unrelated. Reset mock state in beforeEach.
Treating mock as source of truth. The real backend is source of truth. The mock is a temporary stand-in. Verify mock matches reality regularly.
FAQ
What does mocking an API mean?
Replacing real API calls with simulated responses so tests run fast, deterministic, and independent of backend availability or network conditions.
When should I mock APIs in mobile tests?
For unit tests and component tests, always. For integration tests, mock external services and keep internal HTTP real. For E2E, mock sparingly.
What's difference between an API mock and an API stub?
A stub returns canned data. A mock also verifies how it was called. In practice terms get used interchangeably in most testing tools.
What are common API mocking tools for mobile?
WireMock, MockServer, MSW for React Native, OkHttp MockWebServer for Android, URLProtocol-based mocks for iOS, Charles and mitmproxy for E2E.
Why do mocked tests sometimes hide real bugs?
Mocks return what you tell them to. If real API changes shape, mock keeps returning old shape and your tests pass against stale assumptions.
Should I mock third-party SDKs like Firebase or Stripe?
Yes for unit and integration tests. They're slow, costly, and rate limited. For E2E, use their official sandbox or test mode instead of mocks.
β


