# Što ćete izgraditi i zašto je važno#
Testiranje ugovora za React komponente znači validaciju komponente prema realističnom skupu API ponašanja: uspjeh, prazna stanja, validacijske greške, timeoute i padove autorizacije. Fokus nije samo na renderiranju, nego na ugovoru između UI-ja i backenda.
Ovo je važno jer regresije u UI-ju često nastaju zbog “razilaženja mockova”: Storybook koristi jedan lažni odgovor, testovi drugi, a produkcija treći. Rezultat su nestabilan UI, nedosljedna stanja i bugovi koji se pojave tek nakon izdanja.
Ovaj vodič pokazuje praktičan setup u kojem su MSW handleri jedini izvor istine i za Storybook i za testove. Dobivate žive API mockove, pregledive kroz PR-ove i provedive u CI-u.
Ako želite širi kontekst piramide testiranja i toolchaina, prvo pročitajte našu osnovnu strategiju: Strategija testiranja Reacta s Vitestom, React Testing Libraryjem i MSW-om.
# Preduvjeti#
| Zahtjev | Verzija | Napomene |
|---|---|---|
| Node.js | 18+ | Preporučen LTS |
| React | 18+ | Radi i s 19, ali primjeri koriste konvencije iz 18 |
| Storybook | 7.6+ | Primjeri pretpostavljaju modernu Storybook konfiguraciju |
| MSW | 2+ | Koristi http i HttpResponse API-je |
| Vitest | 1.5+ | Radi s React aplikacijama temeljenim na Viteu |
| React Testing Library | 14+ | Za testove usmjerene na korisnika |
# Što “testiranje ugovora” znači na razini komponente#
Testiranje ugovora na razini komponente nije o provjeri ispravnosti backend sheme. Radi se o provjeri ponašanja UI-ja za poznate API ugovore.
Ugovor komponente uključuje:
| Element ugovora | Primjer | Što asertirati |
|---|---|---|
| Oblik zahtjeva | GET /api/orders?page=1 | Komponenta šalje očekivani URL i parametre |
| Oblik odgovora | items, total, paginacija | UI ispravno renderira podatke i paginaciju |
| Ponašanje u grešci | 401, 500, 422 | Ispravne poruke, retry akcije i navigacija |
| Vremenska komponenta | spori odgovori | Loading skeletoni i onemogućene akcije |
| Rubni slučajevi | prazni nizovi, izostavljena opcionalna polja | Tekst za prazno stanje i fallback UI |
Dobar paket contract testova obično pokriva 6 do 12 “UI-smislenih” API scenarija po featureu. To je dovoljno da spriječi većinu regresija bez eksplozije troška održavanja.
🎯 Ključna poruka: MSW handlere tretirajte kao verzionirane, pregledive “API ugovore” za UI, a ne kao potrošne testne stubove.
# Struktura direktorija: jedan izvor istine za mockove#
Najjednostavniji način da spriječite razilaženje jest staviti handlere u zajednički modul i importati ih i u Storybook i u test setup.
Praktična struktura:
| Putanja | Svrha |
|---|---|
src/mocks/handlers/ | Zajednički request handleri, grupirani po domeni |
src/mocks/scenarios/ | Složeni skupovi handlera za specifična stanja |
src/mocks/browser.ts | MSW worker za Storybook i lokalni razvoj |
src/mocks/server.ts | MSW server za unit i integracijske testove |
src/mocks/test-data/ | Factory funkcije i fixturei koje handleri koriste |
src/components/... | Komponente i storyji |
Ova podjela drži “što API radi” u handlerima, a “koje stanje želimo” u scenarijima.
# Korak 1: Definirajte skup handlera po domeni (zajednički)#
Napravite jedan handler file po domeni. Primjer: narudžbe (orders).
// src/mocks/handlers/orders.handlers.ts
import { http, HttpResponse, delay } from 'msw';
type Order = { id: string; status: 'paid' | 'pending'; totalCents: number };
const orders: Order[] = [
{ id: 'ord_1', status: 'paid', totalCents: 1299 },
{ id: 'ord_2', status: 'pending', totalCents: 4999 },
];
export const ordersHandlers = [
http.get('/api/orders', async () => {
await delay(150);
return HttpResponse.json({ items: orders, total: orders.length });
}),
];Nekoliko praktičnih pravila:
- 1Držite handlere blizu načina na koji se stvarni API ponaša. Ako produkcija vraća
itemsitotal, nemojte vraćatidata. - 2Dodajte male odgode (delay) kao default. Mnogi UI bugovi vezani su uz timing i nikad se ne pojave s instant mockovima.
- 3Koristite realistične identifikatore i vrijednosti, ne
id: 1. Želite uhvatiti rubne slučajeve u formatiranju stringova i copyju.
⚠️ Upozorenje: Izbjegavajte pisanje handlera koji su “previše savršeni”. Ako produkcijski API ponekad vraća prazne liste ili djelomična opcionalna polja, modelirajte to kroz scenarije. Savršeni mockovi stvaraju lažnu sigurnost.
# Korak 2: Sastavljajte scenarije umjesto dupliciranja handlera#
Scenariji su imenovani skupovi handlera koji predstavljaju UI stanja. Tu testiranje ugovora postaje održivo.
// src/mocks/scenarios/orders.scenarios.ts
import { http, HttpResponse, delay } from 'msw';
import { ordersHandlers } from '../handlers/orders.handlers';
export const ordersScenario = {
default: () => [...ordersHandlers],
empty: () => [
http.get('/api/orders', async () => {
await delay(100);
return HttpResponse.json({ items: [], total: 0 });
}),
],
serverError: () => [
http.get('/api/orders', async () => {
await delay(100);
return HttpResponse.json({ message: 'Internal error' }, { status: 500 });
}),
],
unauthorized: () => [
http.get('/api/orders', async () => {
await delay(100);
return HttpResponse.json({ message: 'Unauthorized' }, { status: 401 });
}),
],
};Zašto ovo pomaže:
- Storybook storyji se čisto mapiraju na scenarije: “OrdersList Empty”, “OrdersList Error”.
- Testovi mogu ponovno koristiti scenarije bez prepisivanja mockova.
- Kad se API promijeni, ažurirate scenarije i svi potrošači vide promjenu.
# Korak 3: MSW postavke za testove (Vitest)#
Napravite setup testnog servera koji se importa u test runner.
// src/mocks/server.ts
import { setupServer } from 'msw/node';
import { ordersScenario } from './scenarios/orders.scenarios';
export const server = setupServer(...ordersScenario.default());Zatim ga povežite s Vitestom.
// src/test/setup.ts
import '@testing-library/jest-dom/vitest';
import { afterAll, afterEach, beforeAll } from 'vitest';
import { server } from '../mocks/server';
beforeAll(() => server.listen({ onUnhandledRequest: 'error' }));
afterEach(() => server.resetHandlers());
afterAll(() => server.close());Ova konfiguracija radi dvije važne stvari:
onUnhandledRequest: 'error'pretvara nedostajuće mockove u pad testova. To je mehanizam provedbe ugovora.resetHandlers()sprječava “kontaminaciju” između testova, smanjujući nestabilnost.
# Korak 4: MSW postavke za Storybook (isti handleri)#
U Storybooku koristite MSW addon i učitajte iste scenarije handlera.
// .storybook/preview.ts
import type { Preview } from '@storybook/react';
import { initialize, mswLoader } from 'msw-storybook-addon';
import { ordersScenario } from '../src/mocks/scenarios/orders.scenarios';
initialize({ onUnhandledRequest: 'bypass' });
const preview: Preview = {
loaders: [mswLoader],
parameters: {
msw: {
handlers: ordersScenario.default(),
},
},
};
export default preview;Odabir onUnhandledRequest se razlikuje:
- U testovima želite “error” kako biste proveli pokrivenost.
- U Storybooku “bypass” može biti prihvatljiv dok gradite nove komponente, ali timovi često pređu na “warn” kad sazriju.
ℹ️ Napomena: Storybook koristi Service Worker okruženje, dok Vitest koristi Node interceptor. Iste definicije handlera rade u oba slučaja jer MSW apstrahira razlike runtimea.
# Korak 5: Storyji kao živa dokumentacija ugovora#
Sada možete pisati storyje koji eksplicitno deklariraju koji scenarij predstavljaju. Time vaš Storybook postaje katalog ugovora.
// src/components/OrdersList/OrdersList.stories.tsx
import type { Meta, StoryObj } from '@storybook/react';
import { OrdersList } from './OrdersList';
import { ordersScenario } from '../../mocks/scenarios/orders.scenarios';
const meta: Meta<typeof OrdersList> = {
title: 'Orders/OrdersList',
component: OrdersList,
};
export default meta;
type Story = StoryObj<typeof OrdersList>;
export const Default: Story = {
parameters: { msw: { handlers: ordersScenario.default() } },
};
export const Empty: Story = {
parameters: { msw: { handlers: ordersScenario.empty() } },
};
export const ServerError: Story = {
parameters: { msw: { handlers: ordersScenario.serverError() } },
};Praktične prednosti:
- Product i QA mogu pregledati UI stanja bez potrebe za backend seed podacima.
- Developeri mogu odmah reproducirati nezgodna stanja greške.
- Scenariji handlera postaju “živa specifikacija” kako UI očekuje da se API ponaša.
Ako održavate design system, uskladite ova stanja s design tokenima, loading obrascima te empty/error komponentama. Lakše je zadržati konzistentnost kad su tokeni i UI primitive standardizirani: Tokeni design systema s Tailwindom i Radixom.
# Korak 6: Contract testovi koji ponovno koriste scenarije#
Contract test ne bi trebao ponovno graditi mockove. Trebao bi odabrati scenarije i asertirati korisniku vidljivo ponašanje.
Primjer s React Testing Library:
// src/components/OrdersList/OrdersList.test.tsx
import { render, screen } from '@testing-library/react';
import { server } from '../../mocks/server';
import { ordersScenario } from '../../mocks/scenarios/orders.scenarios';
import { OrdersList } from './OrdersList';
it('renders orders from the API', async () => {
server.use(...ordersScenario.default());
render(<OrdersList />);
expect(await screen.findByText('ord_1')).toBeInTheDocument();
expect(screen.getByText('ord_2')).toBeInTheDocument();
});
it('shows an empty state when there are no orders', async () => {
server.use(...ordersScenario.empty());
render(<OrdersList />);
expect(await screen.findByText(/no orders/i)).toBeInTheDocument();
});
it('shows an error message on server error', async () => {
server.use(...ordersScenario.serverError());
render(<OrdersList />);
expect(await screen.findByText(/try again/i)).toBeInTheDocument();
});Nekoliko pravila koja smanjuju nestabilnost:
- Asertirajte tekst koji korisnici vide, ne implementacijske detalje.
- Koristite
findBy...za async sadržaj. - Držite testove fokusirane na ponašanje po scenariju, ne na svaki podkomponentni detalj.
# Kako dijeljenje handlera sprječava razilaženje i nestabilan UI#
Razilaženje nastaje kad imate više izvora istine:
- Testovi mockaju
fetchsitems: [...]. - Storybook vraća
data: [...]. - Produkcija vraća
items: [...]plustotal.
UI “radi” u jednom okruženju i puca u drugom.
Dijeljenje handlera to sprječava jer:
- Svaka promjena oblika odgovora je jedan diff u
src/mocks/handlersilisrc/mocks/scenarios. - Storybook i testovi oba “pucaju” kad se ugovor prekine, što prisiljava usklađivanje.
- Recenzenti mogu uočiti promjene API oblika u PR-ovima, a ne u bug reportovima.
U zrelim timovima promjene handlera tretiraju se kao promjene API-ja: zahtijevaju review i često traže ažuriranje UI-ja i testova u istom PR-u.
# Pojačajte strogoću ugovora: fail na neobrađene zahtjeve#
Strogoća je razlika između “lijepih demo mockova” i testiranja ugovora.
Preporučeni defaulti:
| Okruženje | onUnhandledRequest | Zašto |
|---|---|---|
| Vitest | error | Prisili da svaki request bude u ugovoru |
| Storybook lokalno | warn ili bypass | Pomaže brzini razvoja, ali dugoročno je warn bolji |
| CI Storybook testovi | warn ili error | Sprječava tiho izostavljanje handlera u pregledanim stanjima |
Ako vaše komponente koriste generirane API klijente, stroga MSW pokrivenost rano hvata “iznenadne pozive”, npr. novi endpoint koji se počne pozivati nakon refaktora.
# Workflow: CI, vizualni pregled i sprječavanje regresija#
Dobar workflow testiranja ugovora kombinira:
- 1Automatizirane contract testove u Vitestu.
- 2Vizualni pregled storyja za kritična stanja.
- 3Sprječavanje regresija kroz snapshotove i pregledane promjene mockova.
Primjer CI pipelinea (praktično i brzo)#
Pragmatičan CI pristup za React aplikaciju:
| Korak | Tipično trajanje | Što hvata |
|---|---|---|
| Typecheck i lint | 1 do 3 minute | Nesigurne refaktore, kršenja pravila |
| Unit + contract testovi | 2 do 6 minuta | UI-data regresije, obradu grešaka, loading stanja |
| Storybook build | 2 do 5 minuta | Slomljene storyje, nedostajuće assete |
| Vizualni test run | 3 do 10 minuta | CSS regresije, layout pomake, probleme s temama |
Da pipeline ostane brz, ograničite vizualne testove na ključne storyje, ne na cijelu biblioteku.
CI naredbe (primjer)#
Ove naredbe prikazuju čestu osnovu.
# tests
pnpm test --run
# build Storybook to ensure it compiles
pnpm storybook:buildZa vizualne provjere timovi obično koriste Chromatic, Loki ili Playwright screenshot testove. Detalji ovise o vašem stacku, ali princip je isti: storyji trebaju predstavljati iste MSW scenarije koje koriste vaši testovi.
Sprječavanje regresija: povežite vizualne storyje sa scenarijima#
Napravite mali skup “contract-kritičnih” storyja, npr.:
- Default uspješno stanje
- Prazno stanje
- Error stanje
- Permission denied
- Loading stanje na sporoj mreži
Tih pet stanja obično uhvati velik postotak UI regresija, posebno u aplikacijama bogatim podacima.
Praktična konvencija imenovanja:
| Naziv storyja | Scenarij | Zašto je važno |
|---|---|---|
Default | success | Najčešće korišten put |
Empty | empty | Čest rubni slučaj u novim računima |
ServerError | 500 | Poruke i retry ponašanje |
Unauthorized | 401 | Redirect na login, dozvole |
Loading | long delay | Skeletoni, onemogućene akcije |
Dodajte “Slow” scenarij za UI bugove vezane uz timing#
Timing bugovi su česti u Reactu: dvostruki spinneri, flicker, prerano ponovno omogućavanje gumba. Dodajte spori scenarij i pregledajte ga vizualno.
// src/mocks/scenarios/orders.scenarios.ts
import { http, HttpResponse, delay } from 'msw';
export const slowOrders = () => [
http.get('/api/orders', async () => {
await delay(2500);
return HttpResponse.json({ items: [], total: 0 });
}),
];Koristite ga u storyju naziva Loading, i u testovima asertirajte da se skeletoni renderiraju te da su akcije onemogućene dok je request u tijeku.
Checklist za PR review: ugovor i UI zajedno#
Praktičan proces reviewa je zahtijevati:
- Promjenu handlera ili scenarija kad se promijeni API ponašanje.
- Ažuriranje storyja za svako novo UI stanje.
- Contract test za svaki bug fix koji je uključivao dohvat podataka ili obradu grešaka.
Ovo se dobro uklapa u širu checklistu dizajna i kvalitete: Checklist za React design review: performanse, pristupačnost, održivost.
💡 Savjet: Stavite
src/mocks/**pod CODEOWNERS review, slično kao backend API definicije. To prisiljava da promjene mockova budu namjerne i odvraća od “brzih fixova” koji ruše pokrivenost ugovora.
# Praktični obrasci koji skaliraju izvan jedne komponente#
Obrazac 1: Skupovi scenarija na razini “feature ugovora”#
Za feature stranicu grupirajte scenarije preko više endpointa. Primjer: OrdersPage može zvati narudžbe, korisnički profil i billing status.
Umjesto razbacivanja handlera kroz storyje, napravite feature-level scenarij file koji kompozira domenske handlere.
| Pristup | Prednosti | Nedostaci |
|---|---|---|
| Scenariji po komponenti | Lako za start | Može duplicirati cross-cutting requestove |
| Scenariji po featureu | Odgovara realnim ekranima | Traži koordinaciju između komponenti |
| Hibrid | Najbolje od oba | Treba konvencije i disciplinu u code reviewu |
Obrazac 2: Data factoryji za stabilnost#
Hardkodirani fixturei zastare. Factoryji drže fixturee konzistentnima i omogućuju randomizaciju kad je potrebna.
Pragmatično pravilo: stabilno po defaultu, randomizirano samo u posebnim fuzz testovima.
Obrazac 3: Contract pokrivenost za paginaciju, sortiranje i filtriranje#
Ovo su česti izvori regresija jer ovise o query parametrima.
Jednostavan pristup:
- Napravite handler koji asertira query parametre i vraća različite payloadove.
- Dodajte jedan Storybook story po query varijanti koja vam je bitna.
Čak i mali set storyja i testova ovdje hvata probleme poput pogrešnih naziva parametara i slomljenih labela za sortiranje.
# Česte zamke i kako ih izbjeći#
- 1
Dupliciranje handlera u storyjima i testovima
Rješenje: zajednički moduli handlera + kompozicija scenarija. - 2
Ne rušite testove na neobrađene zahtjeve
Rješenje: provediteonUnhandledRequest: 'error'kako bi se uhvatili nedostajući ugovori. - 3
Mockanje oblika odgovora koji ne odgovaraju produkciji
Rješenje: bazirajte handlere na stvarnim API odgovorima i ažurirajte ih kad se backend promijeni. - 4
Previše storyja bez strategije pregleda
Rješenje: definirajte “contract-kritičan” subset za vizualnu regresiju, a ostatak držite informativnim. - 5
Korištenje instant odgovora posvuda
Rješenje: dodajte defaultni delay i uključite barem jedan spori scenarij po featureu.
# Ključne poruke#
- Definirajte MSW handlere jednom i dijelite ih između Storybooka i testova kako biste eliminirali razilaženje mockova.
- Gradite imenovane scenarije za uspjeh, prazno stanje, grešku, neautorizirano i sporo stanje, pa ih ponovno koristite kroz storyje i contract testove.
- Učinite testove strogima s
onUnhandledRequest: 'error'kako biste proveli pokrivenost ugovora UI-prema-API-ju. - Tretirajte Storybook storyje kao živu dokumentaciju ugovora i u CI-u pokrećite vizualne provjere nad malim, visokovrijednim subsetom.
- Promjene u
src/mocks/**recenzirajte kao produkcijski kod, jer promjene handlera efektivno mijenjaju UI ugovor.
# Zaključak#
Testiranje ugovora za React komponente najbolje funkcionira kada su vaši mockovi zajednički, verzionirani asset. MSW plus Storybook daje vam žive API mockove koji poboljšavaju svakodnevni razvoj i značajno smanjuju nestabilan UI te kasne regresije.
Ako želite pomoć u standardizaciji ovoga kroz React codebase, uključujući Storybook governance, CI vizualnu regresiju i stabilan MSW sloj ugovora, javite se Samiodi i pomoći ćemo vam implementirati održiv workflow testiranja.
FAQ
Osnivač i senior developer u Samiodi. 8+ godina iskustva u izradi React, Next.js, Flutter i n8n rješenja za klijente diljem Europe.
Više iz kategorije Web razvoj
Sve →React obrasci za tablice podataka u velikom opsegu: virtualizacija, pinanje stupaca, filteri i izvoz (TanStack Table + Virtual)
Produkcijski spremni obrasci za React tablice podataka uz TanStack Table i TanStack Virtual: arhitektura stanja, paginacija i sortiranje na strani servera, debounce filteri, pinanje stupaca, izvozi i zamke performansi.
Arhitektura Next.js administratorskog panela za B2B SaaS: RBAC, revizijski zapisi, impersonacija i sigurne masovne radnje
Praktična referentna arhitektura za Next.js admin panele: modeliranje dozvola, revizijski zapisi, sigurna impersonacija i sigurnosne kontrolne liste za masovne radnje, izvoze i rukovanje PII podacima.
Izrada višekoračnog čarobnjaka u Next.js App Routeru uz Server Actions + Zod (bez dodatnog API sloja)
Implementirajte produkcijski spreman Next.js višekoračni obrazac koristeći App Router Server Actions i Zod — s tri strategije stanja (kolačići, DB nacrti, URL), pristupačnim UX‑om, optimističnim prijelazima i robusnim rukovanjem greškama bez dodavanja API sloja.
Trebate pomoć s projektom?
Gradimo prilagođena rješenja koristeći tehnologije iz ovog članka. Senior tim, fiksne cijene.
Povezani članci
Strategija testiranja Reacta u 2026.: Vitest + React Testing Library + MSW za sigurne releaseove
Pragmatična strategija testiranja React aplikacija u 2026. uz Vitest, React Testing Library i MSW. Naučite realističnu testnu piramidu, smanjite flaky testove i isporučujte s pouzdanjem uz CI-spremne obrasce.
React aplikacije prilagođene radu offline uz TanStack Query: perzistencija, ponovni pokušaji i optimistični UI (vodič za 2026.)
Izgradite otporan offline-first UX uz TanStack Query: offline perzistencija za React Query, strategije ponovnih pokušaja i backoffa, sigurni optimistični updateovi, obrasci djelomičnog offline rada i testiranje s MSW-om.
Moderna React frontend arhitektura: moduli po značajkama, granice i skalabilnost
Praktičan vodič za React frontend arhitekturu temeljenu na modulima po značajkama: jasne granice, zajednički slojevi, pravila ovisnosti i održiva strategija strukture mapa za React i Next.js.