Mockuj odpowiedzi API, przechwytując fetch i XHR
Backend nie jest gotowy. Albo jest gotowy, ale nie potrafisz zmusić go, aby na żądanie zwrócił pustą listę, błąd 500 i trzysekundowe opóźnienie. Ten artykuł pokazuje, jak przechwycić fetch i XMLHttpRequest bezpośrednio w przeglądarce za pomocą reguły JS w JustZix, zwracając fałszywy JSON, abyś mógł budować i testować frontend pod dowolny scenariusz, jaki tylko chcesz.
Po co w ogóle przechwytywać w przeglądarce
Zwykła odpowiedź na „potrzebuję fałszywych danych” to serwer mockujący — MSW, json-server, zaślepka w środowisku deweloperskim. Są świetne i dla całego zespołu to właściwy wybór. Ale czasem chcesz czegoś lżejszego:
- Debugujesz produkcję i nie możesz skierować jej na fałszywy backend.
- Chcesz odtworzyć jeden konkretny przypadek brzegowy bez ruszania konfiguracji projektu.
- Przeglądasz cudzą stronę i w ogóle nie masz dostępu do repozytorium.
Reguła JS w JustZix żyje w przeglądarce, stosuje się według wzorca URL i nie wymaga żadnych zmian w projekcie. To jej nisza: szybkie, chirurgiczne, łatwe do wyrzucenia mockowanie.
Owijanie window.fetch
Główna sztuczka to zastąpienie window.fetch własną funkcją, która decyduje, per żądanie, czy zamockować, czy przepuścić. Uruchom to na document_start, aby było zainstalowane zanim jakikolwiek kod aplikacji wywoła 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
};
Budowanie syntetycznego Response
Twoja aplikacja oczekuje prawdziwego obiektu Response — wywoła na nim .json(), sprawdzi .ok, odczyta .status. Konstruktor Response daje Ci to wszystko za darmo:
// Helper: a 200 OK JSON response
function jsonResponse(data, status = 200) {
return new Response(JSON.stringify(data), {
status,
headers: { 'Content-Type': 'application/json' }
});
}
Ponieważ to autentyczny Response, kod, który robi const r = await fetch(...); if (r.ok) return r.json();, działa bez modyfikacji. O to właśnie chodzi — aplikacja nie potrafi zauważyć różnicy.
Symulowanie opóźnień
Puste listy i ścieżki szczęśliwe są łatwe. Błędy żyją w wolnej ścieżce: spinnery, które nigdy nie znikają, wyścigi, podwójne wysyłki. Dodaj opóźnienie przed rozwiązaniem:
// 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: [] });
}
}
Symulowanie kodów błędów
Teraz ścieżki nieszczęśliwe. 500, 404, 401, które powinno odbić Cię do logowania — zwróć je z właściwym statusem, aby Twoja obsługa błędów faktycznie się uruchomiła:
// 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'))
}
Zwróć uwagę na różnicę: 500 nadal rozwiązuje się z r.ok === false, podczas gdy zerwane połączenie odrzuca się. Testuj oba — aplikacje często obsługują jeden, a wywalają się na drugim.
Owijanie XMLHttpRequest
Mnóstwo starszego kodu, a także biblioteki takie jak axios, wciąż używa XMLHttpRequest pod spodem. Przechwytywanie fetch ich nie dotyka. XHR jest bardziej toporny do podrobienia, ale cienka nakładka pokrywa typowy przypadek:
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;
};
Jest to celowo minimalne — pokrywa onload / onreadystatechange z tekstem JSON, czyli to, czego potrzebuje 90% kodu XHR. Jeśli biblioteka inspekcjonuje getAllResponseHeaders(), będziesz musiał zaślepić i to. W tym momencie, szczerze mówiąc, sięgnij po prawdziwy serwer mockujący.
Kiedy zamiast tego użyć prawdziwego serwera mockującego
Przechwytywanie w przeglądarce to właściwe narzędzie do szybkiej, ograniczonej, jednorazowej pracy. Przejdź na MSW lub json-server, gdy:
- Cały zespół potrzebuje tych samych mocków, wersjonowanych w repozytorium.
- Potrzebujesz dopasowania po treści żądania, sekwencji ze stanem albo nagrywania i odtwarzania.
- Twój zestaw testów (Playwright, Cypress) uruchamia mocki w CI.
Zasada kciuka: jeśli mock ma trafić do repozytorium, użyj serwera. Jeśli ma zniknąć, gdy wyłączysz regułę, użyj JustZix.
Zobacz też
- Wymuś wariant testu A/B — zamockuj endpoint flagi, aby przypiąć gałąź.
- Zbuduj watcher ceny i dostępności — obserwuj DOM zamiast sieci.
- Gotowe do wklejenia snippety w sekcji Przykłady.
Przestań czekać na backend. Zainstaluj JustZix, wrzuć nakładkę na fetch do ograniczonej reguły JS i buduj frontend pod każdy scenariusz jeszcze dziś.
Oceń ten wpis
Brak ocen — oceń jako pierwszy.