Isang mesh screen sa isang pipe na humaharang sa mga papasok na arrow at nag-uuri sa mga ito sa success tray at failure tray

Pag-setup ng MSW v2 sa React Native

Bakit MSW sa halip na manual mocks

Nagmo-mock ang karamihan ng React Native projects ng API layer nila gamit ang jest.fn(). Mino-mock mo ang fetch o ang Axios instance mo, sinasabi mo kung ano ang ibabalik nito, tapos doon ka nagte-test.

Gumagana. Hanggang sa hindi na.

Ang problema: tine-test mo ang interaction ng code mo sa isang mock, hindi sa isang HTTP layer. Kung nagbago ang paraan ng paggawa ng URLs ng API client mo, nagdadagdag ng headers, o nagha-handle ng retries, hindi mahuhuli ng mock ang regression. Mas mahalaga pa ito kung nagva-validate ka ng responses sa runtime gamit ang isang bagay tulad ng runtime response validation gamit ang Zod, dahil gusto mong tumakbo ang validation layer sa tunay na response shapes, hindi sa mga gawang-kamay na mock objects. Palaging ibinabalik ng mock ang sinabi mo, kahit ano pa ang talagang ipinadala ng code.

Nag-iintercept ang Mock Service Worker (MSW) ng requests sa network level. Gumagawa ang code mo ng tunay na HTTP calls. Hinuhuli ng MSW ang mga ito bago umalis sa process at ibinabalik nito ang mock responses mo. Lahat ng nasa pagitan ng component mo at ng network ay nae-exercise: ang Redux thunk, ang Axios interceptors, ang error handling, ang response parsing.

Pinapalitan ng manual mocks ang code mo. Pinapalitan ng MSW ang network. Tumatakbo ang code nang eksakto kung paano ito tatakbo sa device, hanggang sa punto kung saan aalis na sana ang request.

Assumptions

Isinulat ang walkthrough na ito para sa:

  • React Native 0.74+ na may default na react-native Jest preset
  • TypeScript na may standard na RN Babel config
  • Redux Toolkit (ito ang ina-assume ng custom render wrapper)
  • Node 18 o mas bago (Node 20 ang rekomendado)

Kung nasa mas lumang RN version ka, isang Expo Jest preset, o walang Redux, ang mga konsepto ay aplikable pa rin pero kakailanganing i-adjust ang ilang snippets.

Installation

Tumatakbo ang MSW v2 sa Jest tests sa pamamagitan ng Node.js server. Hindi relevant ang browser service worker para sa mobile, kaya balewalain mo na lang ang lahat ng sinasabi ng MSW docs tungkol sa service-worker registration.

yarn add -D msw@2.14.6 node-fetch@2.7.0 web-streams-polyfill@4.3.0

Sa mga bersyong ‘yan na-validate ang setup na ito (ang blog-2026-08 tag ng repo); ang @x.y.z ay nagsusulat ng katumbas na caret range sa manifest mo.

Halata na ang msw. Ang node-fetch at web-streams-polyfill ang mga polyfill na kailangan ng MSW v2 sa React Native Jest environment, na iko-configure ko sa susunod na step.

🚩 I-pin ang node-fetch sa v2. Ang v3 pataas ay ESM-only at hindi mailo-load sa pamamagitan ng require() sa isang CommonJS Jest setup file. I-pin sa v2, gaya ng ginagawa ng post na ito, o i-migrate ang polyfills file papuntang ESM. Ang v2 ang mas maikling daan sa isang default na React Native Jest preset.

🚩 Ang mga post na nagsasabing “walang polyfills na kailangan” ay naglalarawan ng ibang environment. Nakabase ang MSW v2 sa Fetch API at Web Streams. May mga Node + Jest combination na mayroon nang mga global na ito; ang React Native Jest preset ay wala. Kapag walang polyfills, makikita mo ang ReferenceError: Response is not defined o TextEncoder is not defined sa unang pagkakataong susubukan ng MSW na gumawa ng response.

Polyfills

Gumawa ng jest.polyfills.cjs sa project root. Kailangan itong .cjs (hindi .ts) dahil nilo-load ito ng Jest bago pa ma-set up ang TypeScript transformer:

/**
 * MSW polyfills para sa React Native.
 * Kailangan para sa Mock Service Worker v2 sa Jest tests.
 */

// TextEncoder / TextDecoder
const { TextEncoder, TextDecoder } = require('util');
global.TextEncoder = TextEncoder;
global.TextDecoder = TextDecoder;

// Fetch API
if (!global.fetch) {
  global.fetch = require('node-fetch');
  global.Headers = require('node-fetch').Headers;
  global.Request = require('node-fetch').Request;
  global.Response = require('node-fetch').Response;
}

// ReadableStream (para sa response streaming)
if (!global.ReadableStream) {
  try {
    const { ReadableStream } = require('web-streams-polyfill');
    global.ReadableStream = ReadableStream;
  } catch {
    // optional ang web-streams-polyfill para sa mas lumang MSW v2
  }
}

Tumatakbo ang file na ito bago mag-load ang test framework, kaya wala pang beforeAll, jest, atbp. dito. Para lang ito sa pag-set up ng globals.

Jest config

I-connect ang polyfills file at isang hiwalay na setup file sa jest.config.cjs. Ito ang minimal na hugis; ang config ng totoong app ay nagpapatong ng sarili nitong transforms at module mappings (sa akin, dagdag ang NativeWind, navigation, at asset mocks):

module.exports = {
  preset: 'react-native',
  testEnvironment: 'node',
  setupFiles: ['<rootDir>/jest.polyfills.cjs'],
  setupFilesAfterEnv: ['<rootDir>/jest.setup.ts'],
  transformIgnorePatterns: [
    // Ini-ignore ng default RN preset ang halos lahat ng node_modules; kailangang ma-transform ang MSW.
    'node_modules/(?!(react-native|@react-native|msw|until-async|rettime|@mswjs|@open-draft|@bundled-es-modules|headers-polyfill|strict-event-emitter|outvariant)/)',
  ],
  moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'],
};

Dalawang key ang gumagawa ng trabaho:

KeyKailan tumatakboGamitin para sa
setupFilesBago ma-install ang Jest frameworkPolyfills, global variables, anumang hindi nangangailangan ng jest/expect
setupFilesAfterEnvPagkatapos ng Jest framework, bago bawat test filebeforeAll/afterEach hooks, MSW server lifecycle, custom matchers

Isa pang gotcha ang linya ng transformIgnorePatterns: nilalaktawan ng default RN preset ang pag-transform ng node_modules, pero may modern syntax ang MSW na hindi kayang patakbuhin ng Jest nang ganoon na lang. Idagdag ang MSW at ang mga untranspiled na dependency nito (msw|until-async|rettime|@mswjs|@open-draft|@bundled-es-modules|headers-polyfill|strict-event-emitter|outvariant) sa allow-list o makikita mo ang SyntaxError: Cannot use import statement outside a module mula sa loob ng node_modules/msw/. Mas marami pa nito ang kasama sa mas bagong MSW versions; kung may pangalan ng package sa error na wala pa sa list mo, idagdag mo ito sa parehong group.

Ang server

Gumawa ng src/test-utils/msw/server.ts:

import { setupServer } from 'msw/node';
import { handlers } from './handlers';

/**
 * MSW server para sa Jest. Sinisimulan/pinapatay sa jest.setup.ts.
 * Gamitin ang `server.use(...errorHandlers)` para i-override sa bawat test.
 */
export const server = setupServer(...handlers);

Kinukuha ng server ang default handlers mo (success responses) at ini-intercept nito ang mga request na tumutugma.

Pag-connect sa lifecycle

Sa jest.setup.ts (na nilo-load ng Jest sa pamamagitan ng setupFilesAfterEnv), simulan ang server bago ang tests, i-reset sa pagitan ng tests, isara pagkatapos:

import '@testing-library/jest-native/extend-expect'; // built-in na sa RNTL >=12.4 ang mga matcher na ito; para lang sa mas lumang RNTL ang import na ito
import { server } from './src/test-utils/msw/server';

// MSW server lifecycle
beforeAll(() => server.listen({ onUnhandledRequest: 'warn' }));
afterEach(() => server.resetHandlers());
afterAll(() => server.close());
HookAno ang ginagawa
beforeAllSinisimulan ang server bago tumakbo ang kahit anong test
afterEachNire-reset ang handlers sa defaults sa pagitan ng tests (para hindi mag-leak ang overrides ng isang test)
afterAllPinapatay ang server pagkatapos makumpleto ang lahat ng tests

Ang onUnhandledRequest: 'warn' na option ay nagla-log ng warning kung gumagawa ang code mo ng request na walang tumutugmang handler. Sa CI, palitan ito ng 'error' para mag-fail ang build sa mga nawawalang handlers:

const onUnhandledRequest = process.env.CI ? 'error' : 'warn';
beforeAll(() => server.listen({ onUnhandledRequest }));

⚠️ Kung gumagamit ang tests mo ng fake timers, i-flush ang pending timers sa afterEach bago i-reset ang handlers. Kung hindi, isang animation timer na naka-schedule sa loob ng isang component ay puwedeng mag-fire pagkatapos magsimula ang susunod na test at mag-trigger ng mga maling failures.

Pagsusulat ng handlers

Bawat handler ay isang function na tumutugma sa isang HTTP method at URL, at nagbabalik ng response.

Isang basic handler para sa REST API:

import { http, HttpResponse } from 'msw';

const BASE_URL = 'https://api.example.com';

export const handlers = [
  http.get(`${BASE_URL}/items`, () => {
    return HttpResponse.json([
      { id: 1, name: 'Item One' },
      { id: 2, name: 'Item Two' },
    ]);
  }),

  http.get(`${BASE_URL}/items/:id`, ({ params }) => {
    const { id } = params;
    return HttpResponse.json({ id: Number(id), name: `Item ${id}` });
  }),

  http.post(`${BASE_URL}/items`, async ({ request }) => {
    const body = await request.json();
    return HttpResponse.json({ id: 3, ...body }, { status: 201 });
  }),
];

Ilang bagay na dapat malaman: ang method-specific helpers (http.get, http.post, at iba pa) ay tumutugma base sa HTTP verb, ang URL params tulad ng :id ay awtomatikong nae-extract papunta sa params, ang request body ay nakukuha sa await request.json(), at ang HttpResponse.json() ay nagbabalik ng typed JSON kasama ang anumang status code na ipasa mo.

Paghihiwalay ng fixtures mula sa handlers

Gumagana ang inline response objects para sa isang sketch. Sa totoong codebase, hindi: lumalabas ang parehong shapes sa handlers, sa component tests, at sa Storybook stories, at ayaw mong mag-maintain ng tatlong kopya.

Ilipat ang fixture data sa sarili nitong file:

// src/test-utils/msw/mockData.ts
export const mockItems = [
  { id: 1, name: 'Item One', createdAt: '2026-01-01T00:00:00Z' },
  { id: 2, name: 'Item Two', createdAt: '2026-01-02T00:00:00Z' },
];

export const mockProfile = {
  id: 'user_1',
  name: 'Warren de Leon',
  email: 'hi@example.com',
};

Magbabasa na ang handlers mula sa mockData:

import { http, HttpResponse } from 'msw';
import { mockItems, mockProfile } from './mockData';

export const handlers = [
  http.get(`${BASE_URL}/items`, () => HttpResponse.json(mockItems)),
  http.get(`${BASE_URL}/me`, () => HttpResponse.json(mockProfile)),
];

Magagamit muli ang parehong fixtures sa component tests kung saan ini-bypass mo ang MSW at direktang ipinapasa ang data. Iisang source of truth.

Handler sets para sa bawat scenario

Ang default success handlers ang simula. Pero kailangang mag-handle ng failures din ang mga tunay na apps. Dito humihinto ang karamihan ng MSW setups. Huwag huminto dito.

Ang mga awkward na bug ang umaabot sa production: ang 401 na bumabalik sa kalagitnaan ng session dahil nag-expire ang token limang minuto na ang nakaraan, ang 429 mula sa biglaang refresh attempts pagkatapos ng maikling network blip, ang 422 na iba ang validation shape kaysa sa inaasahan ng form mo, ang 408 na dapat sanang naging retry pero hindi. Wala kang mahuhuli sa mga ‘yon kung ang tanging error coverage mo ay “paano kung magbalik ng 500 ang API?”.

Gumagawa ako ng hiwalay na handler sets para sa bawat error scenario na kailangang i-handle ng app:

// Success (default)
export const handlers = [...apiHandlers, ...authHandlers];

// Server errors
export const errorHandlers = [
  http.get(`${BASE_URL}/items`, () => {
    return HttpResponse.json(
      { message: 'Internal server error' },
      { status: 500 }
    );
  }),
];

// Unauthorized (expired token)
export const unauthorizedHandlers = [
  http.get(`${BASE_URL}/items`, () => {
    return HttpResponse.json(
      { error: 'invalid_token', message: 'Token has expired' },
      { status: 401 }
    );
  }),
];

// Rate limiting
export const rateLimitHandlers = [
  http.post(`${BASE_URL}/auth/token`, () => {
    return HttpResponse.json(
      { error: 'too_many_requests', message: 'Try again in 60 seconds' },
      { status: 429, headers: { 'Retry-After': '60' } }
    );
  }),
];

// Timeout (sumasagot pagkalipas ng 60s, lampas sa kahit anong client timeout)
export const timeoutHandlers = [
  http.get(`${BASE_URL}/items`, async () => {
    await new Promise(resolve => setTimeout(resolve, 60000));
    return HttpResponse.json({}, { status: 408 });
  }),
];

// Offline (network failure)
export const offlineHandlers = [
  http.get(`${BASE_URL}/items`, () => {
    return HttpResponse.error();
  }),
];

Sa project ko, mayroon akong 11 handler sets:

Handler setStatusAno ang tine-test
handlers200Default success responses
errorHandlers500Server error handling
unauthorizedHandlers401Expired/invalid token flows
forbiddenHandlers403Mga banned/suspended na accounts
conflictHandlers409Duplicate registration
validationErrorHandlers422Form validation errors
rateLimitHandlers429Rate limiting na may Retry-After
emailNotConfirmedHandlers400Kinakailangang email verification
storageErrorHandlers413/404File upload/delete errors
timeoutHandlers408Network timeout simulation
offlineHandlersErrorKumpletong network failure

Bawat set ay nae-export at puwedeng i-swap sa bawat test.

ℹ️ Paano gumagana ang timeout handler. Hinahawakan ng await new Promise(resolve => setTimeout(resolve, 60000)) ang response nang buong minuto, lampas sa kahit anong makatwirang client timeout. Mag-fi-fire muna ang sariling request timeout ng code mo, na siyang path na gustong i-exercise ng test; itakda ang client timeout mo nang mas maikli sa delay, o sasagot lang nang huli na ang handler.

Paggamit ng handlers sa tests

Awtomatikong tumatakbo ang default handlers (naka-register sa setupServer). Para mag-test ng error scenarios, i-override ang mga ito sa bawat test:

import { server } from '@app/test-utils/msw/server';
import { errorHandlers, unauthorizedHandlers } from '@app/test-utils/msw/handlers';

describe('API error handling', () => {
  it('shows error message on server failure', async () => {
    server.use(...errorHandlers);

    // I-render ang component, i-trigger ang fetch, i-assert ang error UI
  });

  it('redirects to login on 401', async () => {
    server.use(...unauthorizedHandlers);

    // I-render ang component, i-trigger ang fetch, i-assert ang redirect
  });

  // Hindi kailangan mag-cleanup - nire-reset ng afterEach sa jest.setup ang handlers
});

Pinapalitan ng spread (...errorHandlers) ang mga tumutugmang handlers. Nananatiling aktibo ang mga handler mula sa default set na hindi tumutugma. Pagkatapos ng test, nire-restore ng server.resetHandlers() ang defaults.

Ang custom render wrapper

Mas maganda ang MSW na may tunay na Redux store, hindi mocked. Ang buong punto ay i-test ang tunay na integration: component → Redux thunk → HTTP request → MSW intercept → response → state update → UI update.

// src/test-utils/renderWithProviders.tsx
import React from 'react';
import { Provider } from 'react-redux';
import { combineReducers, configureStore } from '@reduxjs/toolkit';
import type { RenderOptions } from '@testing-library/react-native';
import { render } from '@testing-library/react-native';

import { itemsReducer } from '@app/features/Items';
import { authReducer } from '@app/features/Auth';

const rootReducer = combineReducers({
  items: itemsReducer,
  auth: authReducer,
});

type RootState = ReturnType<typeof rootReducer>;

function createTestStore(preloadedState?: Partial<RootState>) {
  return configureStore({
    reducer: rootReducer,
    preloadedState,
    middleware: getDefaultMiddleware =>
      getDefaultMiddleware({
        serializableCheck: false,
        immutableCheck: false,
      }),
  });
}

type AppStore = ReturnType<typeof createTestStore>;

interface ExtendedRenderOptions extends Omit<RenderOptions, 'wrapper'> {
  preloadedState?: Partial<RootState>;
  store?: AppStore;
}

export function renderWithProviders(
  ui: React.ReactElement,
  { preloadedState, store, ...options }: ExtendedRenderOptions = {},
) {
  const createdStore = store ?? createTestStore(preloadedState);

  const Wrapper = ({ children }: { children: React.ReactNode }) => (
    <Provider store={createdStore}>{children}</Provider>
  );

  return {
    store: createdStore,
    ...render(ui, { wrapper: Wrapper, ...options }),
  };
}

Na-cover na niyan ang Redux. Karaniwang mas marami pang kailangan ang tunay na apps: i18n, navigation, theming, toast context. Ang wrapper ang tamang lugar para i-compose ang lahat ng ito: i-nest ang bawat provider sa paligid ng {children} eksaktong gaya ng ginagawa ng App.tsx, at balutin ang mga screen na umaasa sa navigation sa isang NavigationContainer na may in-memory navigator. Ang prinsipyo: bawat provider na bumabalot sa app mo sa runtime ay dapat bumalot sa component mo sa renderWithProviders. Anumang makalimutan mo, magiging pagkakaiba ‘yon sa pagitan ng test environment at ng runtime, at ang mga pagkakaibang ‘yon ang dahilan kung bakit nagiging flaky ang tests.

Ngayon nagre-render na ang tests mo na may tunay na store, nagdi-dispatch ng tunay na thunks, at ang MSW ang nagha-handle ng network:

it('loads and displays items', async () => {
  // Nagbabalik ng success response ang default handlers
  const { getByText } = renderWithProviders(<ItemList />);

  await waitFor(() => {
    expect(getByText('Item One')).toBeTruthy();
  });
});

it('shows error state on failure', async () => {
  server.use(...errorHandlers);

  const { getByText } = renderWithProviders(<ItemList />);

  await waitFor(() => {
    expect(getByText('Something went wrong')).toBeTruthy();
  });
});

Walang manual mocking ng dispatch, selectors, o fetch. Tunay ang buong stack maliban sa network.

Inline handler overrides

Minsan kailangan mo ng isang one-off response na hindi kasya sa kahit anong handler set. I-define ito inline:

it('handles unexpected response shape', async () => {
  server.use(
    http.get('https://api.example.com/items', () => {
      return HttpResponse.json({ unexpected: 'shape' });
    })
  );

  // I-test na maayos na hina-handle ng code ang malformed responses
});

Kapaki-pakinabang ito para sa edge cases tulad ng malformed JSON, nawawalang fields, o hindi inaasahang status codes na hindi naman kailangan ng buong handler set.

Pagpapatakbo ng tests

Kapag naka-connect na ang lahat, ganito ang itsura ng isang test file run:

yarn jest src/features/Items/__tests__/ItemList.rntl.tsx
PASS  src/features/Items/__tests__/ItemList.rntl.tsx
  ItemList
    ✓ loads and displays items (218 ms)
    ✓ shows error state on failure (94 ms)

Test Suites: 1 passed, 1 total
Tests:       2 passed, 2 total

Kung makakita ka ng warning tulad ng [MSW] Warning: captured a request without a matching request handler, ginagawa lang ng onUnhandledRequest: 'warn' ang trabaho nito. Magdagdag ng handler para sa URL o ayusin ang request na ginagawa ng code mo.

Kung bumara ang suite, karaniwang naghihintay lang ito sa loob ng mahabang simulated delay. Madalas, ito ay isang timeoutHandlers set na gumagamit ng setTimeout(..., 60000) habang may tunay pang timers ang test environment. Lumipat sa fake timers sa test na iyon (jest.useFakeTimers() tapos jest.advanceTimersByTime(...)) o paikliin ang simulated delay.

Mga karaniwang pagkakamali

Tinutugma ang mga handler ayon sa pagkakasunod-sunod. Kung dalawang handler ang tumutugma sa parehong request, ang una ang mananalo. Kapag tumawag ka ng server.use(...overrides), naunang inilalagay ang mga overrides, kaya mas may priority sila kaysa sa defaults.

Network failure ang sini-simulate ng HttpResponse.error(), hindi HTTP error. Hindi nakakatanggap ng response ang request. Gamitin ito para sa offline scenarios. Para sa HTTP errors (500, 401, at iba pa), gamitin ang HttpResponse.json() na may status code.

Kung nagbabasa ang handler mo ng request body sa pamamagitan ng request.json(), kailangang async ang handler function. Ang paglimot dito ang isa sa mga karaniwang dahilan kung bakit tahimik na nagbabalik ng undefined ang handler.

Warning lang ang default sa mga unhandled request. Ang default na onUnhandledRequest ng MSW ay 'warn', at madaling makaligtaan ang isang warning na dumadaan lang sa CI output. Palitan ito ng 'error' sa CI para mag-fail ang build sa nawawalang handler: ang unhandled request na hindi napapansin ay nangangahulugang pumapasa ang test sa maling dahilan.

Ang error na Response is not defined o TextEncoder is not defined ay nangangahulugang hindi nilo-load ang polyfills file. I-check na nasa Jest config ang setupFiles: ['<rootDir>/jest.polyfills.cjs'], na .cjs ang file extension at hindi .ts, at na tama ang path relative sa rootDir.

Ang SyntaxError: Cannot use import statement outside a module na galing sa node_modules/msw/ (o sa isa sa mga dependency nito) ay nangangahulugang hindi nata-transform ang package na ‘yon. Idagdag ito sa allow-list sa loob ng transformIgnorePatterns; nasa jest.config.cjs ng post na ito ang buong set para sa MSW 2.14.

Hindi kasali ang query strings sa path matching: tumutugma rin ang http.get('/api/items') sa /api/items?page=2, at binabasa ng handler ang mga parameter mula sa request.url. Kung parang binabalewala ng isang test ang query-specific na handler mo, iyon ang dahilan.

Kapag pumapasa ang tests nang local pero bumabagsak sa CI, karaniwang ang onUnhandledRequest: 'error' ang nakahuli ng request na hindi mo namamalayang ginagawa ng code mo sa CI environment, madalas analytics o crash reporting. Magdagdag ka ng handler para rito, o tanggalin ang mga call na ‘yon sa test mode.

Ang kumpletong file structure

project-root/
  jest.config.cjs           # Jest config (preset, setupFiles, setupFilesAfterEnv)
  jest.polyfills.cjs        # TextEncoder, fetch, ReadableStream globals
  jest.setup.ts             # Server lifecycle, custom matchers, global mocks
  src/
    test-utils/
      msw/
        handlers.ts         # Lahat ng handler sets (success, error, 401, etc.)
        server.ts           # setupServer na may default handlers
        mockData.ts         # Fixture data na ginagamit ng handlers
      renderWithProviders.tsx  # Custom render na may tunay na store + providers
      index.ts              # Barrel export

Pinapayagan ng barrel export (index.ts) ang tests na mag-import ng mga karaniwang utilities mula sa iisang lugar. Para sa mga specific handler sets, mag-import nang direkta mula sa handlers file:

import { server, renderWithProviders } from '@app/test-utils';
import { errorHandlers, unauthorizedHandlers } from '@app/test-utils/msw/handlers';

Ano ang ibinibigay sa ‘yo ng setup

Mga 30 minuto lang ang setup. Pagkatapos niyan, mas simple na ang bawat bagong test kaysa sa katumbas nitong manual mock. Nagsusulat ka ng server.use(...errorHandlers) sa halip na jest.fn().mockRejectedValue(new Error('Network error')). Reusable ang handlers sa bawat test file. At tine-test mo ang tunay na integration behaviour, hindi mock behaviour.

Sinasaklaw ng mga handler set sa project ko ang bawat error path na hina-handle ng app. Kapag nagdagdag ako ng bagong API endpoint, nagdadagdag ako ng handlers isang beses, at libre na ang tamang mocking ng bawat test na gumagamit ng endpoint na ‘yon. Bagay din ang parehong handler-set approach sa E2E tests, kung saan ang Detox + Cucumber ang nagpapatakbo ng user flows at isang hiwalay na runtime-mocking layer ang kumokontrol sa API responses. Ang sukatan ng buong setup: mas madali na ngayong isulat ang susunod na test kaysa i-skip ito.

Ang MSW setup sa post na ito (polyfills, Jest wiring, handler sets, at ang custom render wrapper) ay mula sa rn-warrendeleon, ang personal kong React Native project; ang blog-2026-08 tag ang nagmamarka ng eksaktong estadong tinutugma ng mga excerpt na ito. Ang Items feature sa mga halimbawa ay pinasimpleng katapat ng mga totoong feature ng app na iyon.

Warren de Leon
Warren de Leon

Software Engineering Manager. Pinakahuling pinamunuan ang Mobile Platform team sa Hargreaves Lansdown. Sumusulat tungkol sa engineering leadership, React Native, at pagbuo ng magagandang team.

Tingnan ang profile