API Mocking and Stubbing: Which Test Double Do You Need?
A stub answers. A mock answers and checks how it was called. Service virtualization imitates a whole system. Here's how to tell them apart, which one a test needs, and what a Shift-Left API mock server really is.
This is lesson 6.2 of Test Data, Mocks and End-to-End API Testing. In lesson 6.1 you met the mock API; now you sort the family of test doubles apart, so you can pick the one a test needs.
API mocking and stubbing: what's the difference?
API mocking and stubbing both replace a real dependency with a stand-in, but they answer different questions. A stub answers: it returns canned responses so the code under test can carry on. A mock answers and checks: it records how it was called, and the test fails if the calls were wrong. Service virtualization goes further still and imitates a whole system.
The words get blurred in practice. Most tools sold as "mock servers" are HTTP stubs with extra features, and that's fine, as long as you know which job you need done.
The test doubles, side by side
| Double | What it does | The test asserts on | API example |
|---|---|---|---|
| Dummy | Fills a parameter nobody uses | Nothing about it | A placeholder base URL in a test that never calls out |
| Stub | Returns canned answers | What your code did with the answer | GET /products/{id} always returns a fixed product |
| Spy | A stub that also records calls | The recorded calls, after the fact | Count how many times /users/{id} was fetched |
| Mock | Pre-programmed with expected calls; fails if they don't happen | The calls themselves | Expect exactly one POST /orders with quantity: 2 |
| Fake | A working, simplified implementation | Behaviour across several calls | An in-memory store where POST then GET returns what you posted |
The distinction that matters is state verification vs behaviour verification. With a stub, you check the state your code ends up in: the page shows the product, the order total is right. With a mock, you check the behaviour: the right calls went out, in the right order, the right number of times.
A worked example: one orders service, three doubles
Imagine an orders service, built on the sandbox store's API, that looks up the user and the product before it saves an order.
- Stub the product lookup to test pricing logic.
GET /api/v1/products/aaaa…returns a product at 24.99, and the test asserts the order total forquantity: 2is 49.98. The stub never complains about how it was called. - Stub a failure to test resilience. The product lookup returns a 503, and the test asserts the orders service answers with a clear error instead of saving a half-built order. The live sandbox can simulate this with
?error=503:
/api/v1/products?error=503curl -X GET 'https://demo.totalshiftleft.ai/api/v1/products?error=503'- Mock the payment call to test a behaviour that is the requirement: "charge exactly once, even when the client retries". Here a stub isn't enough, because a stub would happily accept two charges. The mock is told to expect one call and fails the test on the second.
Compare the live answer for the same product:
/api/v1/products/aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaacurl -X GET 'https://demo.totalshiftleft.ai/api/v1/products/aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa'The live service returns the Wireless Mouse with its real price. A stub built from the spec returns the same fields with placeholder values. That gap is where integration bugs hide, which is why stubs speed tests up but never replace a run against the real service.
API virtualization vs mocking
Service virtualization imitates a whole dependent system rather than a few endpoints:
| Mocking and stubbing | Service virtualization | |
|---|---|---|
| Scope | A few endpoints, one test or one team | A whole system: many endpoints, several protocols |
| State | Usually none; fixed or rule-based answers | Often stateful: what you create is there on the next call |
| Source | Written by hand or generated from a spec | Often recorded from real traffic, then edited |
| Timing | A fixed delay, if any | Realistic response-time profiles |
| Cost | Minutes to set up | Days to build, ongoing upkeep |
| Pays off for | Unbuilt APIs, flaky partners, error paths | Mainframes, licensed third parties, systems you can't reach from test |
Open-source tools sit at different points on that line: WireMock adds stateful scenarios and record-and-playback to HTTP stubs; Mockoon runs stub servers locally from a desktop app or CLI; Prism serves stubs straight from an OpenAPI spec; in-process libraries such as nock or MSW stub and verify HTTP calls inside a unit test.
Choosing the right double
- Do you only need an answer? Use a stub.
- Is the call itself the requirement (sent once, sent with the right body, never sent)? Use a mock with call verification, usually in a unit test, close to the code.
- Do several calls need to see shared state? Use a fake or virtualization, or test against the real service in an isolated environment.
- Is the real service cheap and reachable? Use it. Every double is a guess about how the real thing behaves.
Common mistakes
- Verifying everything. A mock that checks every header and the order of unrelated calls breaks on every refactor. Verify only the calls that are the behaviour.
- Stubbing with invented data. Hand-written stubs drift from the contract. Generate them from the spec, and regenerate when it changes.
- Calling a stub a mock and expecting verification. If the tool never fails a test because of how it was called, it's a stub.
Mocks and stubs in Shift-Left API
In the test-double vocabulary, a Shift-Left API mock server is a stub server: generated from your spec, with optional conditional and templated responses. It answers; it doesn't verify calls, it doesn't keep state between requests, and it doesn't record or replay traffic. If you need those, a dedicated virtualization tool is the right choice.
What it does give you is the stub generated from the same spec as your tests, and an easy switch between the stub and the real service:
- Click Mocks, select the project, click Generate Mock, tick Products, and click Generate Mocks.
- On the mock for
GET /api/v1/products/{id}, click Test Mock: 200, with placeholders such as a name ofstringand a price of0. - To make a response conditional, open the mock in the response editor and use + Add Condition on a variant, for example a 503 when a header or query field has a given value. Set Delay (ms) to simulate a slow answer.
- Click Start Mock Server, then in Project Settings → Environment Settings add an environment on the mock's path-based URL. Choosing which environment is the default sends the same test to the stub or to the live service.
Lesson 9.9 records that switch step by step, and the mock server feature page lists the editor's options.
Try it yourself
Generate mocks for Products from the sandbox spec and click Test Mock on Get a single product by ID. Then press the first Run button on this page to ask the live sandbox. Same status, same fields, different values: that difference is what a stub can't tell you.
Recap
- API mocking and stubbing both replace a dependency; a stub only answers, while a mock also verifies how it was called.
- API virtualization vs mocking: virtualization imitates a whole, often stateful system, and costs far more to keep accurate.
- Pick the lightest double that answers the test's question, and keep at least one run against the real service.
For the full taxonomy with code for each double, read API mocking vs stubbing: mocks, stubs, fakes and spies.
Try it: Generate mocks for the Products feature of https://demo.totalshiftleft.ai/openapi/rest.json, click Test Mock on the Get a single product by ID mock, then send the same GET to the live sandbox with the first Run button on this page.
- Use this sandbox asset
- https://demo.totalshiftleft.ai/openapi/rest.json
- You got it right when
- Both answer 200 with the same fields: the mock with placeholders (a name of "string", a price of 0), the sandbox with the Wireless Mouse at 24.99.
Frequently asked questions
What is the difference in API mocking and stubbing?+
A stub returns canned answers so the code under test can carry on; the test never asks the stub anything afterwards. A mock also records how it was called and fails the test if the calls were wrong, for example if the orders service never called the users service. Many tools say mock server when they mean an HTTP stub.
What is API virtualization vs mocking?+
Service virtualization imitates a whole dependent system: many endpoints, state that changes between calls, recorded traffic and realistic response times. Mocking usually covers a few endpoints with fixed or rule-based answers for one test or one team. Virtualization costs more to build and maintain, so it pays off for dependencies that are expensive or impossible to reach.
Should I use a stub or a mock for API mocking and stubbing in integration tests?+
Use a stub when you care about what your code does with an answer, such as rendering a 404 page. Use a mock, with call verification, when the call itself is the behaviour, such as making sure a payment is requested exactly once. Most API integration tests need stubs; call verification is usually done in-process, in unit tests.
Related lessons
A mock API answers the way the real one is documented to, so you can test before the service exists or while it's down. Here's what a mock can prove, what it can't, and how to spin one up from a spec.
Run your tests before the real service is ready, or while it's down. Generate mocks from the spec in Shift-Left API, start the mock server, and send a test to it by switching one environment.
API integration testing checks the seams, such as what happens to an order when its user is deleted. Here's how it differs from API testing, the practices that keep it reliable, and a cross-resource test.