Simuler les réponses d'API en interceptant fetch et XHR
Le backend n'est pas prêt. Ou il l'est, mais vous ne pouvez pas lui faire renvoyer une liste vide, une erreur 500 et un délai de trois secondes sur commande. Cet article montre comment intercepter fetch et XMLHttpRequest directement dans le navigateur avec une règle JS JustZix, en renvoyant du faux JSON pour pouvoir construire et tester le frontend contre n'importe quel scénario.
Pourquoi intercepter dans le navigateur
La réponse habituelle à « j'ai besoin de fausses données » est un serveur de mock — MSW, json-server, un stub dans votre environnement de dev. Ils sont parfaits, et pour toute une équipe c'est le bon choix. Mais parfois vous voulez quelque chose de plus léger :
- Vous déboguez la production et vous ne pouvez pas la pointer vers un faux backend.
- Vous voulez reproduire un cas limite précis sans toucher à la configuration du projet.
- Vous examinez le site de quelqu'un d'autre et vous n'avez aucun accès au dépôt.
Une règle JS JustZix vit dans le navigateur, s'applique par motif d'URL, et ne nécessite aucun changement de projet. C'est sa niche : du mocking rapide, chirurgical, jetable.
Envelopper window.fetch
L'astuce centrale est de remplacer window.fetch par votre propre fonction qui décide, par requête, s'il faut simuler ou laisser passer. Exécutez ceci au moment document_start pour qu'il soit installé avant que tout code de l'application n'appelle fetch :
// On sauvegarde l'original pour pouvoir retomber sur le vrai reseau
const realFetch = window.fetch.bind(window);
// Votre table de mocks : correspondance par sous-chaine d'URL -> fabrique de reponse
const mocks = [
{
match: '/api/user/profile',
response: () => jsonResponse({ id: 1, name: 'Ada Lovelace', plan: 'pro' })
},
{
match: '/api/orders',
response: () => jsonResponse([]) // le cas limite de la liste vide
}
];
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); // non simule -> vraie requete
};
Construire une Response synthétique
Votre application attend un véritable objet Response — elle appellera .json(), vérifiera .ok, lira .status. Le constructeur Response vous donne tout cela gratuitement :
// Aide : une reponse JSON 200 OK
function jsonResponse(data, status = 200) {
return new Response(JSON.stringify(data), {
status,
headers: { 'Content-Type': 'application/json' }
});
}
Parce que c'est une véritable Response, du code qui fait const r = await fetch(...); if (r.ok) return r.json(); fonctionne sans modification. C'est tout l'intérêt — l'application ne peut pas faire la différence.
Simuler de la latence
Les listes vides et les chemins heureux sont faciles. Les bugs vivent dans le chemin lent : les spinners qui ne disparaissent jamais, les conditions de course, les doubles soumissions. Ajoutez un délai avant de résoudre :
// Une petite fonction de pause
const sleep = (ms) => new Promise(r => setTimeout(r, ms));
// Dans la table de mocks, on l'attend avant de renvoyer
{
match: '/api/search',
response: async () => {
await sleep(3000); // 3 secondes de "chargement..."
return jsonResponse({ results: [] });
}
}
Simuler des codes d'erreur
Maintenant les chemins malheureux. Une 500, une 404, une 401 qui devrait vous renvoyer à la connexion — renvoyez-les avec le bon statut pour que votre gestion d'erreur s'exécute vraiment :
// 500 Internal Server Error
{
match: '/api/checkout',
response: () => jsonResponse(
{ error: 'payment_gateway_timeout' },
500
)
}
// Un echec reseau (fetch rejette, ne resout pas)
{
match: '/api/flaky',
response: () => Promise.reject(new TypeError('Failed to fetch'))
}
Notez la différence : une 500 résout quand même avec r.ok === false, tandis qu'une connexion coupée rejette. Testez les deux — les applications gèrent souvent l'une et plantent sur l'autre.
Envelopper XMLHttpRequest
Beaucoup de code plus ancien, et des bibliothèques comme axios, utilisent encore XMLHttpRequest en coulisses. L'interception de fetch ne les touche pas. XHR est plus laborieux à falsifier, mais un fin wrapper couvre le cas courant :
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);
// Simule une reponse reussie sans toucher le reseau
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;
};
C'est délibérément minimal — cela couvre onload / onreadystatechange avec du texte JSON, ce dont 90 % du code XHR a besoin. Si une bibliothèque inspecte getAllResponseHeaders(), vous devrez aussi le stubber. À ce stade, honnêtement, prenez un vrai serveur de mock.
Quand utiliser plutôt un vrai serveur de mock
L'interception dans le navigateur est le bon outil pour du travail rapide, ciblé, jetable. Passez à MSW ou json-server quand :
- Toute l'équipe a besoin des mêmes mocks, versionnés dans le dépôt.
- Vous avez besoin d'une correspondance sur le corps de la requête, de séquences à états, ou d'enregistrer-et-rejouer.
- Votre suite de tests (Playwright, Cypress) exécute les mocks en CI.
Règle empirique : si le mock doit être commité, utilisez un serveur. S'il doit disparaître quand vous désactivez une règle, utilisez JustZix.
À voir aussi
- Forcer une variante de test A/B — simulez l'endpoint des flags pour bloquer une branche.
- Construire un surveillant de prix et de stock — observez le DOM plutôt que le réseau.
- Des snippets prêts à coller dans la section Exemples.
Cessez d'attendre le backend. Installez JustZix, déposez un wrapper de fetch dans une règle JS ciblée, et construisez le frontend contre chaque scénario dès aujourd'hui.
Notez cet article
Aucune note — soyez le premier.