Using schema, MSW and MockedProvider the right way
Without mocking, an unfinished backend endpoint blocks the entire frontend development effort. GraphQL mocking at the schema level makes frontend teams independent, enables testing of error scenarios, and significantly speeds up the development cycle.
Table of Contents
- 1. Why GraphQL mocking is more than dummy data
- 2. Schema-based mocking: addMocksToSchema
- 3. MSW in the browser: queries without a real server
- 4. Apollo MockedProvider for component tests
- 5. Mocking error scenarios and partial errors
- 6. Mocking for Magento GraphQL endpoints
- 7. Contract tests between the mock and the real schema
- 8. Comparing mocking strategies
- 9. Summary
- 10. Key takeaways at a glance
- 11. FAQ
1. Why GraphQL mocking is more than dummy data
The most obvious approach in the frontend: create a JavaScript file with hardcoded test data and pass it into component props. That works for individual unit tests, but it fails as soon as a component itself fires a GraphQL query, as soon as network loading times need to be simulated, or as soon as error paths need to be tested. Real GraphQL mocking operates at the level of the network handler or the GraphQL client and intercepts queries exactly the way a real server would.
The benefits go far beyond isolation. A good mock also simulates slow networks, authentication failures, partial data errors within a response, and timeout behavior. Anyone who only works with static fixture files gets reliable tests for the success path, but no confidence for the cases that most often lead to support tickets in production. GraphQL mocking at the schema level also ensures that mocked data is always schema-compliant: a mock that does not match the schema fails early in the development process, not in production.
2. Schema-based mocking: addMocksToSchema
Getting started with schema-based GraphQL mocking begins with the @graphql-tools/mock package. The addMocksToSchema function takes an existing schema object and automatically augments every resolver with realistic default values: strings are mocked as empty strings, integers as 42, booleans as false. For production-ready tests, these defaults are overridden with custom mock resolvers per type and field. The schema itself is either loaded from the SDL or obtained via introspection from the real endpoint.
The decisive advantage of this approach: the mock responses are processed through the full GraphQL execution stack. That means validation, type checking, and field resolution all run exactly as they would on the real server. If a client sends a query that does not match the schema, it also fails against the mock, which moves errors that would otherwise only surface during real API calls into local development time. For teams working schema-first, this is the preferred approach: lock down the schema, spin up a mock server, start frontend development, without waiting for the backend implementation.
# Mock schema for a product catalog (schema-first approach)
# This schema is used by addMocksToSchema
type Query {
product(sku: String!): Product
products(search: String, pageSize: Int): ProductList!
cart(cartId: String!): Cart
}
type Product {
sku: String!
name: String!
price_range: PriceRange!
stock_status: StockStatus!
description: String
}
type ProductList {
items: [Product!]!
total_count: Int!
}
type PriceRange {
minimum_price: Price!
}
type Price {
final_price: Money!
}
type Money {
value: Float!
currency: String!
}
type Cart {
id: String!
items: [CartItem!]!
prices: CartPrices!
}
type CartItem {
uid: String!
quantity: Int!
product: Product!
}
type CartPrices {
grand_total: Money!
}
enum StockStatus {
IN_STOCK
OUT_OF_STOCK
}
3. MSW in the browser: queries without a real server
Mock Service Worker (MSW) is the preferred approach for browser-based GraphQL mocking because it operates on the browser's Service Worker API and thereby intercepts real network requests. The frontend code's behavior does not change at all: it sends real fetch requests, which MSW intercepts and answers with configured responses. Unlike monkey-patching fetch or Axios adapters, MSW works with any HTTP library and also in Playwright E2E tests.
For GraphQL queries, MSW offers the graphql.query() and graphql.mutation() handlers. The handler matches on the operation name, not on the URL alone, which makes it easy to configure multiple endpoints with different responses. In development environments, MSW is activated via service worker registration in the browser; in Vitest or Jest, MSW runs through Node interceptors without a service worker. The same handler code therefore works across every test level, which ensures consistency between unit, integration, and E2E tests.
# Intercepted query: MSW matches on the operation name "GetProduct"
# The handler in handlers.ts returns this structure as the response
query GetProduct($sku: String!) {
product(sku: $sku) {
sku
name
price_range {
minimum_price {
final_price {
value
currency
}
}
}
stock_status
description
}
}
# Error case query: same operation, MSW handler returns errors[]
# Tests error handling in the frontend without changing the server
query GetProductError($sku: String!) {
product(sku: $sku) {
sku
name
}
}
4. Apollo MockedProvider for component tests
Anyone using Apollo Client in a React frontend gets a direct mocking solution for component tests with MockedProvider. The MockedProvider replaces the real Apollo client in the test render tree and answers defined queries with prepared responses. Configuration happens via an array of MockedResponse objects, each of which contains a query operation and the desired response or an error.
A common pitfall with Apollo MockedProvider: queries must match exactly, including variables. If the query used in the test does not match the one in the component bit for bit, for example due to different fragment definitions or variable defaults, the MockedProvider returns no response and the test fails with a timeout error. It is therefore good practice to define GraphQL documents as .graphql files that are imported both in production code and in tests. In addition, the addTypename: false parameter of the MockedProvider offers a simplification for tests that do not need __typename fields.
5. Mocking error scenarios and partial errors
GraphQL errors have a special structure: unlike HTTP errors, GraphQL responses can contain both data and errors at the same time. A partial error occurs when a resolver for an optional field fails while other fields are populated correctly. In this case, the frontend has to decide whether and how to display the partial data. This scenario is barely testable with static fixture data; a good mock setup makes it possible to deliberately generate such responses.
With MSW, both data and errors are returned in the response object to simulate partial errors. In Apollo MockedProvider, the error property of a MockedResponse is used for network errors, and the result.errors array is used for GraphQL errors within a successful HTTP response. Anyone who does not model this distinction in tests gets no statement about whether the frontend reacts correctly to Apollo's error boundary behavior, or whether a loading indicator gets stuck when a resolver silently fails.
# Partial error: cart is returned correctly,
# but the field "shipping_addresses" fails in the resolver.
# MSW handler returns this structure to test frontend behavior.
# Expected response structure (JSON):
# {
# "data": {
# "cart": {
# "id": "abc123",
# "items": [...],
# "prices": { "grand_total": { "value": 89.99, "currency": "EUR" } },
# "shipping_addresses": null
# }
# },
# "errors": [
# {
# "message": "Shipping address resolver failed",
# "path": ["cart", "shipping_addresses"],
# "extensions": { "category": "graphql-authorization" }
# }
# ]
# }
query GetCart($cartId: String!) {
cart(cartId: $cartId) {
id
items {
uid
quantity
product { sku name }
}
prices {
grand_total { value currency }
}
shipping_addresses {
city
postcode
}
}
}
6. Mocking for Magento GraphQL endpoints
With Magento GraphQL, a few specific challenges come into play that make mocking more complex. Magento queries run against a single endpoint (/graphql), but use store-specific headers such as Store: de and optional authentication bearer tokens for customer context. MSW handlers can evaluate these headers and deliver different mock responses for guest and customer sessions. This is especially valuable when testing headless frontends that handle cart, checkout, and customer account flows via GraphQL.
Another Magento-specific aspect: many production Magento GraphQL queries use inline fragments on union types such as ProductInterface to distinguish between SimpleProduct, ConfigurableProduct, and other product types. In the mock, the __resolveType field must be set correctly so that the GraphQL execution stack knows which concrete type to resolve. Schema-based mocking with addMocksToSchema solves this automatically, provided the mock resolver for ProductInterface returns a concrete type.
# Typical Magento query with union type inline fragments
# Must return __resolveType correctly in the mock
query GetProductDetails($sku: String!) {
products(filter: { sku: { eq: $sku } }) {
items {
__typename
sku
name
price_range {
minimum_price {
final_price { value currency }
}
}
... on ConfigurableProduct {
configurable_options {
attribute_code
label
values { uid label swatch_data { value } }
}
variants {
product { sku name }
attributes { uid label code value_index }
}
}
... on SimpleProduct {
stock_status
only_x_left_in_stock
}
}
}
}
7. Contract tests between the mock and the real schema
Contract tests close the dangerous gap between mocks and reality: they ensure that the mocked schema matches the real production schema. Without contract tests, mocks can go stale: a mutation gains new required fields, a type gets renamed, a field receives a different return type. The mock-based component test keeps running green while the production code breaks.
The tool of choice is GraphQL Inspector, which compares two schemas and identifies breaking changes. In the CI process, a job loads the current schema from the production endpoint via introspection and compares it with the schema the mock handlers are based on. If there are discrepancies, the CI build fails before the code goes to production. For Magento projects, it is also advisable to store the schema snapshot in the repository, so that schema diffs can be detected even without network access to the production system.
8. Comparing mocking strategies
Different test levels call for different GraphQL mocking strategies. Choosing the right approach depends on the test goal, the GraphQL client in use, and the desired level of isolation.
| Strategy | Use case | Advantage | Limitation |
|---|---|---|---|
| addMocksToSchema | Local mock server, Storybook | Schema-validated, all types automatic | No network interception |
| MSW (graphql.query) | Unit, integration, E2E tests | Real network stack, client-agnostic | No schema validation by default |
| Apollo MockedProvider | React component tests | Exact query control, cache state | Apollo-specific, requires exact match |
| Fixtures (JSON) | Simple unit tests, snapshots | Fast, no setup required | No error scenarios, goes stale quickly |
| Contract tests (Inspector) | CI pipeline, schema drift detection | Prevents mock-reality drift | Requires schema access in CI |
In practice, several strategies are combined: addMocksToSchema for the local development server and Storybook, MSW for Vitest and Playwright tests, and Apollo MockedProvider for isolated component tests. The contract tests run once a day in the CI pipeline and raise an alarm on schema drift before a new frontend release is deployed.
9. Summary
Effective GraphQL mocking for frontend and API tests requires more than static fixture files. Schema-based mocking with addMocksToSchema ensures that mock responses are always schema-compliant. MSW enables real network interception that works consistently across unit, integration, and E2E tests. Apollo MockedProvider gives full control over Apollo cache state and query matching in component tests. Combining these strategies reduces flaky tests, covers error paths, and makes frontend teams independent of the backend's development status.
Contract tests between the mock schema and the production schema are the decisive safeguard against the most common long-term mistake: mocks that stay green while the real schema has long since evolved. With GraphQL Inspector in the CI process, this drift is detected automatically before it turns into a production problem.
GraphQL Mocking for Frontend and API Tests: Key Takeaways at a Glance
Schema mocking
addMocksToSchema validates mock responses against the real schema, preventing mock drift in local development and in Storybook.
MSW for every test level
Mock Service Worker intercepts real network requests, works client-agnostically, and is consistent across unit, integration, and E2E tests.
Error paths
Deliberately mock partial GraphQL errors (data + errors) to test error handling, loading indicator behavior, and error boundaries in the frontend.
Contract tests
GraphQL Inspector in CI compares the mock schema with the production schema, raising an alarm on breaking changes before they reach production.