Simulare le risposte API intercettando fetch e XHR
Il backend non e pronto. Oppure e pronto, ma non puoi farlo restituire una lista vuota, un errore 500 e un ritardo di tre secondi a comando. Questo articolo mostra come intercettare fetch e XMLHttpRequest direttamente nel browser con una regola JS di JustZix, restituendo JSON falso cosi puoi costruire e testare il frontend contro qualsiasi scenario tu voglia.
Perche intercettare nel browser
La risposta abituale a "mi servono dati falsi" e un server di mock — MSW, json-server, uno stub nel tuo ambiente di sviluppo. Sono ottimi, e per un intero team sono la scelta giusta. Ma a volte vuoi qualcosa di piu leggero:
- Stai facendo debug della produzione e non puoi puntarla a un backend falso.
- Vuoi riprodurre un caso limite specifico senza toccare la configurazione del progetto.
- Stai revisionando il sito di qualcun altro e non hai alcun accesso al repository.
Una regola JS di JustZix vive nel browser, si applica per pattern di URL e non richiede alcuna modifica al progetto. Questa e la sua nicchia: mocking rapido, chirurgico e perfetto per cose usa-e-getta.
Avvolgere window.fetch
Il trucco centrale e sostituire window.fetch con una tua funzione che decide, per ogni richiesta, se simulare o lasciar passare. Esegui questo a document_start cosi e installato prima che qualsiasi codice dell'app chiami fetch:
// Save the original so we can fall through to the real network
const realFetch = window.fetch.bind(window);
// Your mock table: match by URL substring -> response factory
const mocks = [
{
match: '/api/user/profile',
response: () => jsonResponse({ id: 1, name: 'Ada Lovelace', plan: 'pro' })
},
{
match: '/api/orders',
response: () => jsonResponse([]) // the empty-list edge case
}
];
window.fetch = async (input, init) => {
const url = typeof input === 'string' ? input : input.url;
const hit = mocks.find(m => url.includes(m.match));
if (hit) {
console.log('[mock] intercepted', url);
return hit.response();
}
return realFetch(input, init); // not mocked -> real request
};
Costruire una Response sintetica
La tua app si aspetta un vero oggetto Response — chiamera .json(), controllera .ok, leggera .status. Il costruttore Response ti da tutto questo gratis:
// Helper: a 200 OK JSON response
function jsonResponse(data, status = 200) {
return new Response(JSON.stringify(data), {
status,
headers: { 'Content-Type': 'application/json' }
});
}
Poiche e una Response autentica, il codice che fa const r = await fetch(...); if (r.ok) return r.json(); funziona senza modifiche. E proprio questo il punto — l'app non riesce a distinguere la differenza.
Simulare la latenza
Le liste vuote e i percorsi felici sono facili. I bug vivono nel percorso lento: spinner che non spariscono mai, race condition, doppi invii. Aggiungi un ritardo prima di risolvere:
// A small sleep helper
const sleep = (ms) => new Promise(r => setTimeout(r, ms));
// In the mock table, await it before returning
{
match: '/api/search',
response: async () => {
await sleep(3000); // 3 seconds of "loading..."
return jsonResponse({ results: [] });
}
}
Simulare i codici di errore
Ora i percorsi infelici. Un 500, un 404, un 401 che dovrebbe rispedirti al login — restituiscili con lo status giusto cosi la tua gestione degli errori gira davvero:
// 500 Internal Server Error
{
match: '/api/checkout',
response: () => jsonResponse(
{ error: 'payment_gateway_timeout' },
500
)
}
// A network failure (fetch rejects, not resolves)
{
match: '/api/flaky',
response: () => Promise.reject(new TypeError('Failed to fetch'))
}
Nota la differenza: un 500 si risolve comunque con r.ok === false, mentre una connessione caduta viene rifiutata. Testa entrambi — le app spesso gestiscono uno e vanno in crash sull'altro.
Avvolgere XMLHttpRequest
Molto codice piu vecchio, e librerie come axios, usano ancora XMLHttpRequest sotto il cofano. L'intercettazione di fetch non li tocca. XHR e piu goffo da falsificare, ma un wrapper sottile copre il caso comune:
const RealXHR = window.XMLHttpRequest;
window.XMLHttpRequest = function () {
const xhr = new RealXHR();
const realOpen = xhr.open;
let mockedUrl = null;
xhr.open = function (method, url, ...rest) {
if (url.includes('/api/legacy')) mockedUrl = url;
return realOpen.call(this, method, url, ...rest);
};
const realSend = xhr.send;
xhr.send = function (...args) {
if (!mockedUrl) return realSend.apply(this, args);
// Fake a successful response without hitting the network
setTimeout(() => {
Object.defineProperty(xhr, 'readyState', { value: 4 });
Object.defineProperty(xhr, 'status', { value: 200 });
Object.defineProperty(xhr, 'responseText', {
value: JSON.stringify({ legacy: true })
});
xhr.onreadystatechange && xhr.onreadystatechange();
xhr.onload && xhr.onload();
}, 0);
};
return xhr;
};
Questo e volutamente minimale — copre onload / onreadystatechange con testo JSON, che e cio di cui ha bisogno il 90% del codice XHR. Se una libreria ispeziona getAllResponseHeaders() dovrai stubbare anche quello. A quel punto, onestamente, ricorri a un vero server di mock.
Quando usare invece un vero server di mock
L'intercettazione nel browser e lo strumento giusto per lavoro rapido, circoscritto e usa-e-getta. Passa a MSW o json-server quando:
- L'intero team ha bisogno degli stessi mock, versionati nel repository.
- Ti serve il matching della richiesta sul body, sequenze con stato, o registrazione e riproduzione.
- La tua suite di test (Playwright, Cypress) esegue i mock in CI.
Regola pratica: se il mock dovrebbe essere committato, usa un server. Se dovrebbe svanire quando disattivi una regola, usa JustZix.
Vedi anche
- Forzare una variante di A/B test — simula l'endpoint dei flag per bloccare un ramo.
- Costruisci un monitor di prezzo e disponibilita — osserva il DOM invece della rete.
- Snippet pronti da incollare nella sezione Esempi.
Smetti di aspettare il backend. Installa JustZix, inserisci un wrapper di fetch in una regola JS circoscritta e costruisci il frontend contro ogni scenario gia da oggi.
Valuta questo articolo
Nessuna valutazione — sii il primo.