Quick answer: You do not need to mock an entire API to test the one behavior that matters. A partial mock intercepts only the routes under test and forwards everything else to a real service through proxy pass-through, so the team gets deterministic control where it is needed and real behavior everywhere else. The risk to manage is making the boundary explicit: a request must be either mocked or forwarded, never ambiguously both, and the passthrough target must be a safe environment so tests do not accidentally write to production. Partial mocks are the pragmatic middle ground between a full mock that drifts and a live dependency that is hard to control.

Full mocks are expensive to maintain because every endpoint has to be reproduced, and they drift the moment the API changes. Pure live testing is unreliable because the team cannot force the specific failure or edge case under test. A partial mock resolves the tension by owning only the routes the test manipulates, a webhook failure, a slow response, a specific edge case, and proxying the rest to a real upstream. The application behaves as if it talks to the genuine API while the test keeps deterministic control over the part it actually cares about. This dramatically reduces the surface area of mock code that must be kept in sync.

Full mock, partial mock, and live only

The three strategies trade maintenance against control in different proportions.

DimensionFull mockPartial mock + proxyLive only
Mock surfaceAll endpointsRoutes under testNone
MaintenanceHighLowNone
Control of edge casesFullOn mocked routesNone
Realism elsewhereMockedLive via proxyFully live
Drift riskHighLimitedNone

Configure the mock-or-forward boundary

A declarative configuration names which routes are intercepted and where the rest are forwarded, so the routing is reviewable and the passthrough points at a safe environment.

# partial mock: intercept what the test needs, forward the rest
mock:
  baseUrl: http://127.0.0.1:4010
  passthrough:
    target: https://api.staging.example.com  # never production
  routes:
    - path: /v1/payments
      method: POST
      fault:
        status: 500        # force the failure under test
    - path: /v1/payments
      method: GET
      latencyMs: 3000
  # every other route is proxied to the real staging service

Because the rule is explicit, a request cannot silently fall through to an unintended target, and the mock can log whether each request was handled locally or forwarded, which makes debugging the routing straightforward.

Keep forwarded traffic safe and observable

Passthrough should point at staging or a sandbox, and tests should use non-destructive identifiers so forwarded requests never mutate real customer data. Record which requests were proxied so the test output shows the full path, and pin the passthrough target in configuration rather than letting it default. A safe guard is to fail fast if the target is not on an approved list.

function resolvePassthrough(configured) {
  const allowed = [
    "https://api.staging.example.com",
    "https://api.sandbox.example.com",
  ];
  if (!allowed.includes(configured)) {
    throw new Error(
      "Refusing to proxy outside approved staging/sandbox targets"
    );
  }
  return configured;
}

Let the spec decide what is worth mocking

A local-first OpenAPI workflow can generate the partial mock from the spec, validating the intercepted routes against the documented schema while the proxy preserves real behavior elsewhere. This keeps the mock surface small and honest, and it scales better than a hand-maintained full mock. The underlying judgment is about where determinism actually matters: mock the specific behavior the test asserts, and let the rest of the system behave genuinely. That focus is what keeps mock code minimal, trustworthy, and free of the drift that large mock surfaces tend to accumulate.

Frequently asked questions

When does a partial mock beat a full mock?

When only a few routes drive the behavior under test and the rest are incidental. Mocking the whole API adds maintenance without adding control over the thing the test asserts.

Can the passthrough accidentally hit production?

It can if the target is misconfigured, so pin the target in config and fail fast unless it is on an approved staging or sandbox list. Never let the passthrough default to whatever the application uses.

How do I know which requests were mocked or forwarded?

Have the mock log each request with its routing decision, and include that in the test output. Visibility into the boundary removes ambiguity when a test behaves unexpectedly.

Does proxying introduce flakiness from the live service?

It can add dependency on staging availability, but the routes that carry the asserted behavior stay mocked and deterministic. If the live service is unstable, expand the mock surface to cover the flaky routes.

This is part of the Powerduck vs Mockoon comparison — start there for the full decision around mock servers and how they are configured.