Network interception and mocking are critical techniques for creating robust, isolated frontend tests that don't depend on live backend services. By strategically intercepting and simulating network requests, you'll gain unprecedented control over your testing environment, enabling more predictable and comprehensive test scenarios.
Prerequisites#
- Node.js (v16+ recommended)
- TypeScript (v4.5+)
- A modern frontend framework (React, Vue, Angular)
- Jest or Vitest for testing
- Recommended tools:
- Playwright
- MSW (Mock Service Worker)
- Axios or Fetch API
- Estimated setup time: 30-45 minutes
Understanding Network Interception#
Network interception allows you to capture, modify, or replace network requests during testing. This technique provides complete control over your application's network interactions without relying on external services.
Why Network Mocking Matters#
Frontend applications frequently depend on external APIs, making tests unpredictable and fragile. By mocking network requests, you can:
- Simulate various server responses
- Test error handling scenarios
- Eliminate external dependencies
- Speed up test execution
- Create deterministic test environments
💡 Pro Tip: Network mocking isn't just about testing—it's about creating a controlled, reproducible testing environment.
Implementing Network Interception with MSW#
Mock Service Worker (MSW) provides a powerful, flexible approach to network interception. Here's a comprehensive implementation:
// networkInterceptor.tsimport { rest } from "msw";import { setupServer } from "msw/node";// Define mock handlers for different scenariosconst productHandlers = [rest.get("/api/products", (req, res, ctx) => {return res(ctx.status(200),ctx.json([{ id: 1, name: "Laptop", price: 999.99 },{ id: 2, name: "Smartphone", price: 599.99 },]),);}),rest.post("/api/products", (req, res, ctx) => {const newProduct = req.body;// Simulate product creation with validationif (!newProduct.name || !newProduct.price) {return res(ctx.status(400), ctx.json({ error: "Invalid product data" }));}return res(ctx.status(201), ctx.json({ ...newProduct, id: Date.now() }));}),];// Create server with handlersexport const server = setupServer(...productHandlers);
// networkInterceptor.tsimport { rest } from "msw";import { setupServer } from "msw/node";// Define mock handlers for different scenariosconst productHandlers = [rest.get("/api/products", (req, res, ctx) => {return res(ctx.status(200),ctx.json([{ id: 1, name: "Laptop", price: 999.99 },{ id: 2, name: "Smartphone", price: 599.99 },]),);}),rest.post("/api/products", (req, res, ctx) => {const newProduct = req.body;// Simulate product creation with validationif (!newProduct.name || !newProduct.price) {return res(ctx.status(400), ctx.json({ error: "Invalid product data" }));}return res(ctx.status(201), ctx.json({ ...newProduct, id: Date.now() }));}),];// Create server with handlersexport const server = setupServer(...productHandlers);
Advanced Interceptor Configuration#
// enhancedInterceptor.tsimport { rest } from "msw";import { setupServer } from "msw/node";type MockScenario = "success" | "error" | "network-failure";export function createMockServer(scenario: MockScenario = "success") {const handlers = [rest.get("/api/data", (req, res, ctx) => {switch (scenario) {case "success":return res(ctx.status(200), ctx.json({ data: "Mocked Success" }));case "error":return res(ctx.status(400), ctx.json({ error: "Bad Request" }));case "network-failure":return res.networkError("Connection Failed");}}),];return setupServer(...handlers);}
// enhancedInterceptor.tsimport { rest } from "msw";import { setupServer } from "msw/node";type MockScenario = "success" | "error" | "network-failure";export function createMockServer(scenario: MockScenario = "success") {const handlers = [rest.get("/api/data", (req, res, ctx) => {switch (scenario) {case "success":return res(ctx.status(200), ctx.json({ data: "Mocked Success" }));case "error":return res(ctx.status(400), ctx.json({ error: "Bad Request" }));case "network-failure":return res.networkError("Connection Failed");}}),];return setupServer(...handlers);}
⚠️ Warning: Always reset MSW handlers between tests to prevent cross-test pollution.
Intercepting Complex Network Scenarios#
Error Handling and Edge Cases#
// complexInterception.tsasync function fetchProductWithRetry(productId: number,retries = 3,): Promise<Product> {try {const response = await fetch(`/api/products/${productId}`);if (!response.ok) {throw new Error("Product fetch failed");}return response.json();} catch (error) {if (retries > 0) {return fetchProductWithRetry(productId, retries - 1);}throw error;}}
// complexInterception.tsasync function fetchProductWithRetry(productId: number,retries = 3,): Promise<Product> {try {const response = await fetch(`/api/products/${productId}`);if (!response.ok) {throw new Error("Product fetch failed");}return response.json();} catch (error) {if (retries > 0) {return fetchProductWithRetry(productId, retries - 1);}throw error;}}
Troubleshooting Network Interception#
Best Practices#
- Always mock entire request/response cycles
- Use TypeScript interfaces for mock data
- Implement comprehensive error scenarios
- Keep mock data realistic and representative
- Use environment variables to toggle mocking
- Minimize external dependencies in tests
- Regularly update mock data to reflect API changes
Next Steps#
- Explore advanced Playwright network interception
- Learn about contract testing with OpenAPI
- Investigate GraphQL mocking techniques
- Study performance testing with network simulation
- Implement end-to-end testing strategies