skip to content

Mocking Layers: Module vs Network

You will learn to place your fake at the right seam — the module boundary, the network boundary, or a real test server. Interviewers ask this to see whether you know that mocking your own API client can make a test pass while the application is broken.

on this pageshow

questions

4

Why can a frontend test that mocks the application's own API-client module stay green while the feature is broken for real users, and what changes when the fake is moved down to the network boundary instead?

level: middleimportance: must knowfreq 62%

answer

  1. where is the line drawn
  2. fake deletes your own code
  3. request and parsing never ran
  4. interceptor sees the real request
  5. fixture is still your own belief

basics

~20 s

Mocking your own client deletes the code that talks to the server, so the test can only confirm your assumptions about it. Faking at the network boundary keeps that code running and replaces only the server, so request shape, parsing and error handling are exercised for real.

solid answer

~40 s

A module-level fake replaces code you own, which means the test proves your app agrees with your own fixture rather than with the server. Request construction, serialization, status handling and response parsing are all deleted from the run, so a wrong URL or a renamed field passes unnoticed — the classic false green. Moving the fake to the network boundary flips that: the test intercepts the outgoing HTTP request, so the real client code builds the request and parses the reply, and only the server is imaginary. You get much higher fidelity and a fake that survives refactors of the data layer, at the cost of a bit more setup and slower tests. Module mocking still earns its place for non-HTTP collaborators, third-party SDKs whose transport you cannot see, and deliberately narrow unit tests.

go deeper

for a junior

Know the two places a fake can sit — replacing your own module versus answering the request — and be able to say that only the second one lets the app's real request-and-parse code run.

for a middle

Explain the mechanism both ways: which lines each seam deletes, why a wrong URL or renamed field survives a module fake, and what the interceptor lets you assert about the outgoing request.

for a senior

Demonstrate placement judgment on a real suite: default to the network seam for HTTP, keep the module seam for non-HTTP collaborators, and be explicit that neither seam proves the server still sends what your handlers claim.

for a principal

Set the policy and its cost: the seam choice determines how much of a refactor breaks the suite and which class of production bugs the suite can never catch, so decide where the organisation pays for higher-fidelity coverage instead.

## Two different things called "mocking the API" Both phrases mean "the server is not real", but they cut the application at completely different places. **Module seam.** You replace one of your own modules — the api client, a data-fetching hook's dependency, a repository object — with a fake object that returns canned values. The substitution happens inside the JavaScript module graph, before any network work exists. **Network seam.** You leave every module in place and intercept the outgoing HTTP request, answering it with a response you control. The substitution happens at the boundary the browser would actually cross. Everything else — fidelity, coupling, refactor cost — follows from where that line is drawn. ## Why the module seam produces false greens A fake does not add behaviour; it removes it. With the client module faked, the following never executes: the path and query string the app would build, the headers it would attach, the body it would serialize, the status codes it would branch on, the payload it would parse, and the field mapping it would apply. What the test asserts is that the UI works *given a value you wrote by hand*. That makes the test a statement about your beliefs, not about the system. Two very common production bugs slip straight through: - The app requests the wrong thing — a mistyped query parameter, a missing path segment, a lost auth header — and the fake answers correctly regardless, because it ignores or barely inspects its arguments. - The server's payload changes — a renamed field, a new nullable, a list that became paginated — and the fixture, frozen at the shape you once believed in, keeps every test green. The suite passes; the feature is broken; nobody learns anything until a user reports it. ## What the network seam restores Intercept at the network boundary and the same test suddenly exercises the real client: - the URL, method, headers and query string are genuinely built and can be asserted on, because the interceptor sees the actual request; - the request body is genuinely serialized; - the response is genuinely parsed and mapped by your code, so a fixture that does not match the app's expectations *fails the test* instead of bypassing the mismatch; - status handling and error mapping run, so "404 means empty state" is proved rather than assumed; - any interceptor, caching or retry layer in the client runs too. The imaginary part shrinks to exactly one thing: the server. That is the smallest honest fake for a frontend feature test, and it is why network-level interception has become the default recommendation for component and integration tests. ## The second benefit: coupling A module fake is written against the *shape of your implementation* — a module path and an exported function signature. Change the shape and the tests break even though nothing a user can see has changed: renaming the module, splitting one client into two, replacing one HTTP library with another, or moving from hand-written calls to a generated client. Hundreds of red tests for a pure refactor is a coupling signal, not a safety signal. A network-level fake is written against the *URL and payload*, which is a contract with a party outside your codebase and therefore far more stable. It keeps working across data-layer refactors, and it stops working precisely when the thing it describes — the API — actually changes. That is the behaviour you want from a test. ## What the network seam costs It is not free, and pretending otherwise is its own red flag: - **Setup.** Something must install the interception and tear it down between tests; leaked handlers across tests are a real flakiness source. - **Speed.** Slightly slower than returning a value from a fake function, and you now execute serialization and parsing on every test. - **Debuggability.** A failure can now come from the handler, the client, or the component, so the failure message is less pinpointing than a module fake's. - **Fidelity is still not truth.** You wrote the responses. The seam removes a class of bugs about *your* code; it does not tell you whether the server still returns what you claim. ## When a module seam is still the right choice Say this part out loud; an interviewer is listening for dogma. - The collaborator is **not HTTP**: a clock, a random source, a browser API, an analytics facade, a WebSocket wrapper, a payment SDK that opens its own iframe. - The dependency is a **third-party SDK whose transport you do not control or want to model** — faking its documented method is more honest than reverse-engineering the requests it makes. - The test is a **deliberately narrow unit test** of logic above the client, where you want the failure to be unambiguous and you accept the narrower claim. - You need to **force a state that is awkward to express as a response** — though most of those, including failures and delays, are expressible at the network seam and belong there. ## The answer an interviewer wants Name the seam, say what each one deletes, and connect it to the two failure modes: fidelity (false greens) and coupling (refactor breakage). Then place them: network seam by default for anything that goes over HTTP, module seam for non-HTTP collaborators and narrow unit tests, and a small number of tests against a real server to keep the fixtures honest.

  • If the network seam is higher fidelity, why not use it for absolutely every test?
    Because not every dependency is HTTP, and not every test wants the extra surface. A clock, a browser API or a third-party SDK with its own transport is faked more honestly at the module seam. And a narrow unit test benefits from a fake that makes the failure unambiguous — at the network seam a red test could be the handler, the client or the component.
  • Does moving the fake to the network boundary remove the risk of your fixtures drifting from the real API?
    No. It removes the risk that your own request-building and parsing code is wrong, because that code now runs. The response bodies are still written by you, so if the server renames a field and you do not update the handler, everything stays green. Closing that gap needs something that sees the real server.
  • Where does a GraphQL client fit — module seam or network seam?
    Still the network seam. GraphQL over HTTP is a POST you can intercept, so the client's document, variables, normalization and error handling all keep running. Faking the client's own hook or link instead deletes exactly the layer most likely to be wrong, which is the same trap as faking a REST client.
  • An interviewer says their team asserts the fake client was called with the right arguments — does that recover the lost coverage?
    Partly, and only against your own expectation. It checks that the app asked for what you think it should ask for, which is worth something. It says nothing about whether the server accepts that request, and it tests nothing about parsing the response, so the whole reading half of the client stays uncovered.

Faking your api client is like testing a vending machine by handing the customer a drink over the top: the coin slot, the keypad and the dispenser are never tried. Faking at the network boundary is refilling the machine with your own stock and letting the customer use it normally.

saying these in an interview costs you the question

  • Treats mocking the client and mocking the network as the same thing
  • Says the fake returns what the server would, so fidelity is equal
  • Claims network interception makes tests immune to API drift
  • Believes any mocking at all makes a test worthless
  • Mocks the HTTP library rather than intercepting the request and calls it the network layer

context

open as a page

A frontend component test replaces the app's own API-client module with a fake that returns a canned user object. Which parts of the application's code does that test no longer exercise?

level: juniorimportance: should knowfreq 45%

basics

~20 s

Everything below the fake goes untested: URL and query building, headers and auth, request serialization, response parsing, status and error mapping, plus any caching or retry logic inside the client. Only the code above the seam actually runs.

open as a page

A frontend team replaces its HTTP client library with a different one and changes no user-visible behavior, yet hundreds of tests fail. What does that tell you about where those tests placed their fakes, and where should the seam have been?

level: seniorimportance: should knowfreq 40%

basics

~20 s

The fakes were bound to the implementation — the client library or the app's own module — so swapping it invalidated them even though behavior was unchanged. A fake placed at the network boundary describes URLs and payloads instead, and survives any client that speaks HTTP.

open as a page

Your frontend test suite fakes the network in every single test, so no test ever talks to a real server. What fidelity risk have you accepted, and how would you decide where in the portfolio to pay for a real backend?

level: principalimportance: should knowfreq 33%

basics

~20 s

You have accepted that every response in the suite is your own belief about the API, so the whole suite can stay green while the server changes underneath it. Closing that requires a small, deliberately funded band of tests running against a real backend on the riskiest flows.

open as a page