β€’
Drizz raises $2.7M in seed funding β€’
β€’
Featured on Forbes
β€’
Drizz raises $2.7M in seed funding β€’
β€’
Featured on Forbes
Logo
Schedule a demo
Blog page
>
Mocking APIs in Mobile Tests: When to Do It, When not To

Mocking APIs in Mobile Tests: When to Do It, When not To

When mocking APIs in mobile tests speeds you up and when it lies to you. Patterns for unit, integration, and E2E layers with mobile-specific traps.
Author:
Asad Abrar
Posted on:
June 30, 2026
Read time:

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.

Layer Mock APIs? Why
Unit tests Yes, always Speed. Logic isolation. No network.
Component tests Yes, usually Test rendering and state handling, not network.
Integration tests Mock external services, real internal Test your code's HTTP layer integration, but isolate third parties.
E2E happy path Use real APIs in a staging env Whole point is to prove system works together.
E2E error paths Mock at network edge Real APIs won't return 503 on demand.

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.

‍

About the Author:

Asad Abrar
Co-founder & CEO, Drizz
Ex-Coinbase PM and IIT Kharagpur grad killing flaky mobile tests by day, and obsessing over F1 lap timings by night.
Schedule a demo