100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
Testing

Stubbing and Mocking API Responses

Learn how to replace real backend responses with controlled, static or dynamic data using cy.intercept() to test UI states reliably.

Network & FixturesIntermediate9 min readJul 10, 2026
Analogies

Why Stub Responses at All

Stubbing means intercepting a network request and returning a response you define instead of letting it reach the real server, which gives you full control over the data your UI renders during a test. This is critical for testing edge cases like empty lists, error states, or paginated results that would be difficult or slow to reproduce against a live backend, since you'd otherwise need to manipulate database state or wait for specific conditions to occur naturally. Stubbing also removes backend latency and flakiness from your test suite, making runs faster and more deterministic across CI environments.

🏏

Cricket analogy: Stubbing is like a coach running a specific fielding drill by feeding balls from a bowling machine set to a fixed line and length, rather than waiting for a live bowler to naturally produce that exact delivery.

Static vs. Dynamic Stubs

A static stub returns the same fixed body and status code every time, defined either inline as an object or loaded from a fixture file with { fixture: 'file.json' }, and is the simplest way to lock in a known UI state. A dynamic stub uses a handler function passed as the second argument to cy.intercept(), where you inspect the incoming req and call req.reply() with a response computed at runtime, which is necessary when the response needs to vary based on the request body, a counter, or test-specific logic such as simulating pagination across multiple calls to the same endpoint.

🏏

Cricket analogy: A static stub is like a fixed net-practice setup where the bowling machine always delivers the same line and length, while a dynamic stub is like a coach adjusting the machine's settings live based on which batter steps up.

javascript
// Static stub using an inline body
cy.intercept('GET', '/api/cart', {
  statusCode: 200,
  body: { items: [], total: 0 }
}).as('emptyCart');

// Static stub loaded from a fixture file
cy.intercept('GET', '/api/products', { fixture: 'products.json' }).as('getProducts');

// Dynamic stub that varies the response based on request count
let callCount = 0;
cy.intercept('GET', '/api/notifications', (req) => {
  callCount += 1;
  req.reply({
    statusCode: 200,
    body: { unread: callCount === 1 ? 3 : 0 }
  });
}).as('getNotifications');

cy.visit('/inbox');
cy.wait('@getNotifications');
cy.get('[data-testid="badge"]').should('contain', '3');

Simulating Errors and Delays

You can force an error state by setting statusCode to 500, 404, or 403 in the stub, which lets you verify your application's error handling and toast messages without needing the backend to actually fail. Cypress also supports a delay property on the response object, or the deprecated cy.wait(ms) approach for artificial network latency, letting you assert that loading spinners appear and disappear correctly. Combined with forceNetworkError: true, you can simulate a complete connection failure to test retry logic or offline banners.

🏏

Cricket analogy: Forcing a 500 error is like a groundskeeper deliberately flooding the pitch before a match to test how the team and broadcasters handle an abandoned game, rather than waiting for real rain.

Use { statusCode: 500, body: { error: 'Internal Server Error' } } combined with a delay of a few hundred milliseconds to reliably test both the error message and the loading spinner's visibility window in a single assertion sequence.

Over-stubbing every endpoint in every test can hide real integration bugs, since your UI may pass tests against a stub that no longer matches the actual API contract. Balance stubbed unit-style component tests with a smaller number of true end-to-end tests that hit a real or staging backend.

  • Stubbing replaces a real network response with one you control, enabling deterministic tests for hard-to-reproduce states.
  • Static stubs return fixed data via an inline object or a fixture file reference.
  • Dynamic stubs use a handler function to compute a response at runtime, useful for pagination or counters.
  • Error states can be simulated by setting statusCode to values like 404, 403, or 500.
  • The delay property or forceNetworkError: true can simulate latency or complete connection failure.
  • Over-reliance on stubs risks masking real API contract drift, so pair with some real-backend tests.

Practice what you learned

Was this page helpful?

Topics covered

#Testing#CypressStudyNotes#TestingQA#StubbingAndMockingAPIResponses#Stubbing#Mocking#API#Responses#APIs#StudyNotes#SkillVeris