Mock Network Latency Distributions in Axios
Mocking network latency distributions in Axios testing allows
developers to simulate realistic, unpredictable network conditions—such
as jitter, tail latency (p95/p99), and timeouts—rather than relying on
static delay values. By integrating statistical distribution models into
tools like Axios interceptors, axios-mock-adapter, or Mock
Service Worker (MSW), teams can rigorously validate how client-side
applications handle race conditions, loading states, retry policies, and
slow connections.
1. Define the Latency Distribution Model
Real-world network requests do not have fixed response times. Instead, latency typically follows statistical distributions:
- Uniform Distribution: Latency randomly varies between a minimum and maximum threshold (\(t \in [t_{min}, t_{max}]\)). Useful for modeling standard network jitter.
- Normal (Gaussian) Distribution: Latency clusters around a mean (\(\mu\)) with a specific standard deviation (\(\sigma\)).
- Log-Normal or Pareto Distribution: Best for modeling realistic internet latency, where most requests are fast, but a long tail of requests experience significant delays (e.g., p99 spikes).
Here is a mathematical helper for generating sample distributions in JavaScript:
// Log-normal distribution generator
function getLogNormalDelay(meanMs, stdevMs) {
const variance = stdevMs ** 2;
const mu = Math.log((meanMs ** 2) / Math.sqrt(variance + meanMs ** 2));
const sigma = Math.sqrt(Math.log(variance / (meanMs ** 2) + 1));
// Box-Muller transform for normal random variable
const u1 = Math.random();
const u2 = Math.random();
const z0 = Math.sqrt(-2.0 * Math.log(u1)) * Math.cos(2.0 * Math.PI * u2);
return Math.exp(mu + sigma * z0);
}2. Implement Latency Simulation in Axios
Approach A: Using Custom Axios Interceptors
Axios interceptors provide a native, dependency-free way to intercept requests or responses and insert artificial, distributed delays.
import axios from 'axios';
const api = axios.create({ baseURL: 'https://api.example.com' });
api.interceptors.response.use(async (response) => {
if (process.env.NODE_ENV === 'test') {
// Generate a delay from a log-normal distribution (mean: 200ms, stdev: 50ms)
const delay = getLogNormalDelay(200, 50);
await new Promise((resolve) => setTimeout(resolve, delay));
}
return response;
});Approach B: Using
axios-mock-adapter
When testing without a real backend, axios-mock-adapter
allows dynamic handlers that resolve after a computed delay.
import axios from 'axios';
import MockAdapter from 'axios-mock-adapter';
const mock = new MockAdapter(axios);
mock.onGet('/users').reply(async () => {
const delay = getLogNormalDelay(150, 40);
await new Promise((resolve) => setTimeout(resolve, delay));
return [200, [{ id: 1, name: 'Alice' }]];
});Approach C: Using Mock Service Worker (MSW)
MSW intercepts requests at the network level and provides a dedicated
delay utility that accepts variable values.
import { http, HttpResponse, delay } from 'msw';
export const handlers = [
http.get('https://api.example.com/users', async () => {
const dynamicDelay = getLogNormalDelay(300, 100);
await delay(dynamicDelay);
return HttpResponse.json([{ id: 1, name: 'Alice' }]);
}),
];3. Testing Real-World Scenarios
Once variable latency is integrated into the test environment, configure test suites to evaluate specific resilience behaviors:
- Timeout Handling: Set
axios.defaults.timeout = 500and generate latency samples with high standard deviation to ensure timeout errors (codeECONNABORTED) are caught and handled. - Request Cancellation: Verify that components abort
pending Axios requests (via
AbortController) when unmounted during long-tail latency events. - Out-of-Order Execution (Race Conditions): Send consecutive requests with high variance to test whether responses arriving out of sequence correctly resolve without corrupting application state.
- UI Indicators: Validate that loading skeletons or spinners render for the duration of the dynamic delay and properly dismiss when the promise resolves.