# Što ćete naučiti#
Strategija testiranja Reacta u 2026. manje je pitanje odabira alata, a više odabira pravih granica (boundaries). Vitest donosi brzinu, React Testing Library čini testove user-centric, a MSW stabilizira “API površinu” bez vezanja test suitea uz dostupnost backenda.
Ovaj vodič donosi pragmatičnu testnu piramidu, konkretne primjere za unit, integracijske i “contract-ish” API testove, kao i anti-patterns, rješenja za flakiness te CI preporuke koje možete primijeniti odmah.
Možda će vam trebati i šira QA perspektiva kroz platforme i automatizaciju u našem vodiču strategija QA testiranja za agencije.
# Pragmatična testna piramida za React aplikacije#
Klasična testna piramida i dalje funkcionira, ali React aplikacije često “puknu” kad timovi pokušaju raditi “sve integracijske testove” ili “sve E2E”. Cilj je brza povratna informacija tijekom razvoja i visoko povjerenje kod releasa.
Evo praktične piramide za većinu produktnih timova koji isporučuju tjedno ili dnevno:
| Sloj | Ciljani udio | Tipično trajanje po testu | Što pokriva | Alati |
|---|---|---|---|---|
| Unit testovi | 60–70% | 1–20ms | Čista logika, mapiranje podataka, validacija, formatiranje | Vitest |
| Integracijski testovi | 25–35% | 20–300ms | Ponašanje komponenti, forme, navigacija, async UI stanja | Vitest + React Testing Library |
| “Contract-ish” API testovi | 5–10% | 50–500ms | Frontend očekivanja endpointa, shape errora, caching ponašanje | MSW + Vitest |
| E2E smoke | 3–10 kritičnih flowova | sekunde | Plaćanje, signup, checkout, dozvole | Playwright ili Cypress |
Zašto ovo radi: vrh piramide je skup i flaky. Ako previše toga gurnete gore, pipeline se uspori, a tim počne ignorirati padove.
🎯 Ključna poruka: Optimizirajte stabilan i brz core suite koji se vrti na svakom PR-u, a najsporije testove svedite na kratku listu release-kritičnih flowova.
# Osnovni setup: Vitest, RTL i MSW#
Većina React aplikacija u 2026. je ili Vite-based ili Next.js-based. Vitest se najbolje slaže s Vite projektima, ali radi i za component pakete te mnoge Next.js postavke uz pravilnu konfiguraciju.
Dependencies i konfiguracija#
Instalirajte osnove:
npm i -D vitest @vitest/coverage-v8 jsdom \
@testing-library/react @testing-library/jest-dom @testing-library/user-event \
mswMinimalna Vitest konfiguracija za DOM okruženje:
// vitest.config.ts
import { defineConfig } from 'vitest/config';
export default defineConfig({
test: {
environment: 'jsdom',
setupFiles: ['./test/setup.ts'],
globals: true,
css: false,
coverage: {
provider: 'v8',
reporter: ['text', 'html', 'lcov'],
lines: 80,
functions: 75,
branches: 70,
statements: 80,
},
},
});I vaš setup file:
// test/setup.ts
import '@testing-library/jest-dom/vitest';Razumna struktura foldera#
Dosljednost je važnija od osobnih preferencija. Ovo dobro radi u većim sustavima:
| Područje | Predložena lokacija | Napomena |
|---|---|---|
| Unit testovi | src/**/__tests__/*.test.ts | Držite blizu koda |
| Testovi komponenti | src/**/__tests__/*.test.tsx | Preferirajte integracijski stil |
| Shared test utili | test/utils/* | Render wrapperi, factoryji |
| MSW handleri | test/msw/handlers.ts | Centralno mjesto za API ponašanje |
| MSW server | test/msw/server.ts | Jedan server, koristi se kroz testove |
# Unit testovi: brzi, čisti i dosadni#
Unit testovi su mjesto gdje kupujete brzinu. Ne bi trebali renderirati React ako to možete izbjeći. Ako funkcija prima ulaze i vraća izlaze, testirajte je na toj granici.
Primjer: mapiranje API podataka u view model#
// src/domain/mapUser.ts
export type ApiUser = { id: string; full_name: string; created_at: string };
export type User = { id: string; name: string; createdAt: Date };
export function mapUser(u: ApiUser): User {
return { id: u.id, name: u.full_name, createdAt: new Date(u.created_at) };
}// src/domain/__tests__/mapUser.test.ts
import { describe, it, expect } from 'vitest';
import { mapUser } from '../mapUser';
describe('mapUser', () => {
it('mapira API polja u model aplikacije', () => {
const user = mapUser({
id: 'u1',
full_name: 'Ada Lovelace',
created_at: '2026-01-10T12:00:00.000Z',
});
expect(user.name).toBe('Ada Lovelace');
expect(user.createdAt).toBeInstanceOf(Date);
expect(user.createdAt.toISOString()).toBe('2026-01-10T12:00:00.000Z');
});
});Gdje unit testovi najviše vrijede#
Unit testovi su high leverage za:
| Područje | Zašto je važno | Tipični bugovi koje sprječava |
|---|---|---|
| Validacijske sheme | Edge caseovi s vremenom eksplodiraju | Neispravne predaje, broken backfillovi |
| Pravila cijena/popusta | Poslovno kritična logika | Bugovi koji utječu na prihode |
| Provjere dozvola | Sigurnost i kontrola pristupa | Curanje podataka, pokvarene role |
| Transformacije podataka | API se mijenja, UI očekuje stabilnost | Greške renderiranja, krive oznake |
Ako koristite Zod za forme i validaciju, testove shema držite čiste i iscrpne. Za obrasce koji se dobro skaliraju, pogledajte naš vodič React forme u većim sustavima s React Hook Form i Zod.
💡 Savjet: Kad bug pobjegne u produkciju, pitajte “bi li ovo mogao uhvatiti čisti unit test?” Ako da, dodajte taj test i držite ga izvan React renderiranja. Tako suite ostaje brz.
# Integracijski testovi: testirajte UI kao korisnik#
Integracijski testovi su mjesto gdje React Testing Library briljira. Cilj nije testirati implementacijske detalje. Cilj je dokazati da korisnik može odraditi zadatke, uz to da UI ispravno reagira na promjene stanja i async ponašanje.
Render helper spreman za produkciju#
Većini aplikacija trebaju provideri: router, query client, i18n, theme. Centralizirajte to jednom.
// test/utils/render.tsx
import { render } from '@testing-library/react';
import type { ReactNode } from 'react';
function Providers(props: { children: ReactNode }) {
return props.children;
}
export function renderApp(ui: ReactNode) {
return render(ui, { wrapper: Providers });
}Držite ga minimalnim i dodajte providere samo kad su nužni. Pretrpani wrapperi skrivaju ovisnosti i čine testove težima za razumjeti.
Primjer: integracijski test za flow slanja forme#
Test pokriva UI stanja: inicijalni prikaz, tipkanje, submit, loading, poruku uspjeha. Ne provjerava interni state niti hook pozive.
// src/features/profile/ProfileForm.test.tsx
import { screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { describe, it, expect } from 'vitest';
import { renderApp } from '../../../test/utils/render';
import { ProfileForm } from './ProfileForm';
describe('ProfileForm', () => {
it('šalje valjane podatke i prikazuje uspjeh', async () => {
const user = userEvent.setup();
renderApp(<ProfileForm />);
await user.type(screen.getByLabelText('Full name'), 'Ada Lovelace');
await user.click(screen.getByRole('button', { name: 'Save' }));
expect(await screen.findByText('Saved')).toBeVisible();
});
});Da bi integracijski testovi bili stabilni, slijedite tri pravila:
- 1Queryajte po role i accessible name, ne po class nameovima.
- 2Assertajte ono što korisnik vidi, ne interni state.
- 3Preferirajte
findByza async UI i izbjegavajte ručne sleepove.
Testiranje async UI-ja bez flakinessa#
Flaky testovi često dolaze iz grešaka tipa “assert prerano”. Preferirajte:
await screen.findByText('...')za async rezultateawait waitFor(() => expect(...))za uvjete- izbjegavajte fiksne timeoutove poput
setTimeoutilisleep
⚠️ Upozorenje: Izbjegavajte
getByTextodmah nakon async akcije koja pokreće request. Ako se UI ažurira nakon ticka,getByTextće baciti error i test će povremeno padati ovisno o opterećenju stroja.
# “Contract-ish” API testovi s MSW-om: zaključajte frontend očekivanja#
MSW je sweet spot između unit testova i punog end-to-end testiranja. Presrećete requestove na mrežnoj granici i vraćate determinističke odgovore. To omogućuje testiranje ponašanja frontenda kad backend vraća stvarne shapeove, errore i latenciju.
Ovi testovi su “contract-ish” jer validiraju pretpostavke poput:
- URL i metoda endpointa
- potrebni headeri
- shape responsea i error handling
- caching ključevi i invalidation ponašanje
Ne zamjenjuju backend contract testing, ali sprječavaju tihe UI regresije kad se API promijeni.
MSW setup za Vitest#
Napravite server i handlere.
// test/msw/server.ts
import { setupServer } from 'msw/node';
import { handlers } from './handlers';
export const server = setupServer(...handlers);// test/msw/handlers.ts
import { http, HttpResponse } from 'msw';
export const handlers = [
http.get('/api/profile', () => {
return HttpResponse.json({ full_name: 'Ada Lovelace' });
}),
http.post('/api/profile', async ({ request }) => {
const body = await request.json();
if (!body || !body.full_name) {
return HttpResponse.json(
{ error: { code: 'VALIDATION', message: 'Full name required' } },
{ status: 400 }
);
}
return HttpResponse.json({ ok: true });
}),
];Povežite MSW u Vitest lifecycle:
// test/setup.ts
import '@testing-library/jest-dom/vitest';
import { afterAll, afterEach, beforeAll } from 'vitest';
import { server } from './msw/server';
beforeAll(() => server.listen({ onUnhandledRequest: 'error' }));
afterEach(() => server.resetHandlers());
afterAll(() => server.close());Postavka onUnhandledRequest: 'error' značajno diže razinu povjerenja. Osigurava da test suite padne ako aplikacija pozove endpoint koji niste mockali, što često znači neočekivan code path.
Primjer: provjera semantike requesta i error UI-ja#
Ovaj primjer pokazuje kako testirati da vaš UI ispravno reagira na backend validacijski error, koristeći realan HTTP interaction obrazac.
// src/features/profile/ProfileForm.msw.test.tsx
import { screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { describe, it, expect } from 'vitest';
import { renderApp } from '../../../test/utils/render';
import { ProfileForm } from './ProfileForm';
describe('ProfileForm API contract-ish behavior', () => {
it('prikazuje validacijski error s API-ja', async () => {
const user = userEvent.setup();
renderApp(<ProfileForm />);
await user.clear(screen.getByLabelText('Full name'));
await user.click(screen.getByRole('button', { name: 'Save' }));
expect(
await screen.findByText('Full name required')
).toBeVisible();
});
});Ako želite provjeravati headere ili payload, MSW podržava inspekciju requesta. Držite asertacije fokusirane na “što je bitno za ispravnost”, a ne na svako polje.
Dodajte realističnu latenciju da uhvatite loading bugove#
Čest produkcijski bug: UI nikad ne prikaže spinner, gumbi se mogu dvaput kliknuti ili se pojave race conditioni. Dodajte male determinističke delayeve u specifične handlere kad treba.
| Scenarij | Preporučeni delay | Što otkriva |
|---|---|---|
| Spam klikovi na Save | 150–300ms | Dupli submitovi, nedostaje disable |
| Skeleton rendering | 200–500ms | Layout shiftovi, nedostaje loading state |
| Retry/backoff UI | 300–800ms | Retry logika, error boundaries |
Delayeve koristite štedljivo i nikad ne koristite random jitter u testovima.
# Česti anti-patterns (i što umjesto njih)#
Većina React test suiteova postane bolna zbog nekoliko predvidljivih grešaka. Njihovo ispravljanje često značajno smanji runtime i flakiness.
Anti-pattern: snapshot testiranje svega#
Snapshot testove je lako napraviti, a teško održavati. Također često promaše regresije u korisničkom ponašanju.
Snapshot koristite samo za:
- stabilan, neinteraktivan output (npr. renderiranje markdowna)
- kritično formatiranje koje je namjerno striktno
Umjesto toga, asertajte vidljiva ponašanja:
- disabled stanje gumba
- prikazana poruka greške
- ispravna navigacija
Anti-pattern: mockanje React internals i hookova#
Mockanje useState, useEffect ili dubokih child komponenti često stvara testove koji prolaze iako je UI slomljen. Također poskupljuje refaktore.
Umjesto toga:
- mockajte na granici, najčešće mrežu s MSW-om
- injectajte ovisnosti za čistu logiku i unit testirajte to zasebno
Anti-pattern: testiranje implementacijskih detalja#
Ako je test vezan uz DOM strukturu ili CSS selektore, padat će na bezazlenim refaktorima.
Preferirajte queryje ovim redoslijedom:
- 1
getByRoles accessible name - 2
getByLabelTextza inpute - 3
getByTextkad predstavlja korisniku vidljiv tekst - 4
getByTestIdsamo kao zadnju opciju
Anti-pattern: shared state između testova#
Shared state stvara order-dependent padove. Ovo je veliki izvor “radi na mom računalu” CI failova.
Tipični popravci:
- resetirajte MSW handlere nakon svakog testa
- resetirajte storeove između testova
- izbjegavajte mutiranje module singletona
ℹ️ Napomena: Ako vaša aplikacija koristi globalni store, osigurajte factory koji kreira svježi store po testu. Ne importajte i ne koristite jednu instancu storea kroz cijeli test suite.
# Flaky testovi: uzroci i praktični popravci#
Flakiness nije misterij. Dolazi iz async timinga, shared statea, ovisnosti o stvarnoj mreži i nedeterminističkih podataka.
Checklist za flakiness koji možete primijeniti u 10 minuta#
| Simptom | Vjerojatan uzrok | Rješenje |
|---|---|---|
| Pada samo na CI-ju | sporiji CPU, headless razlike | koristite findBy, uklonite sleepove, povećajte default timeoute samo kao zadnju opciju |
| Pada kad se vrti kao suite | test pollution | resetirajte handlere, resetirajte store, izbjegnite globalne mutacije |
| Random timing failovi | race conditioni | awaitajte user eventove, awaitajte async UI, uklonite pogrešnu upotrebu act |
| “Unhandled request” errori | nedostaje MSW handler | dodajte handler ili popravite korištenje endpointa |
| Failovi vezani uz timezone/datum | locale razlike | zamrznite vrijeme u testovima za date logiku |
Ako trebate zamrznuti vrijeme za date-sensitive logiku:
// in a test file
import { vi } from 'vitest';
vi.useFakeTimers();
vi.setSystemTime(new Date('2026-01-01T00:00:00.000Z'));Koristite ovo samo u testovima kojima treba, i nakon toga vratite timere.
# CI preporuke: brza povratna informacija bez “crvenih buildova”#
Dobar CI setup pretvara test suite u produktni asset. Loš CI setup pretvara testove u šum.
Praktične pipeline faze#
| Faza | Vrti se na | Što radi | Tipični budžet |
|---|---|---|---|
| Lint + typecheck | svaki PR | sprječava očite kvarove | 1–3 minute |
| Unit + integracija | svaki PR | Vitest paralelno | 2–6 minuta |
| “Contract-ish” MSW suite | svaki PR | može biti isti run, tagirano | uključeno gore |
| E2E smoke | main branch ili nightly | samo kritični flowovi | 5–15 minuta |
| Security provjere | main branch | provjere ovisnosti i konfiguracije | 2–10 minuta |
Za security aspekt, uskladite QA sa secure defaultima i threat modelingom. Naš checklist za sigurnost web aplikacija dobar je dodatak vašim release gateovima.
Preporučene Vitest naredbe#
Pokrenite testove s coverageom:
npx vitest run --coverageFail fast na flaky retryjima samo tijekom istrage. U normalnom radu, retryji skrivaju stvarne probleme. Ako baš morate, koristite retries kratko i pratite ih kao tech debt.
Coverage pragovi: koristite ih kao guardrailove, ne kao ciljeve#
Coverage nije kvaliteta, ali može spriječiti regresije tipa “isporučili smo bez testova”. Krenite s realnim pragovima i postupno ih dižite.
Dobar baseline za produktne aplikacije:
- lines: 80%
- branches: 70%
- functions: 75%
Fokusirajte se na risk hotspotove umjesto lova na 100%. Plaćanja, dozvole i pricing zaslužuju viši coverage od layout komponenti.
Paralelizacija i splitanje testova#
Ako je suite duži od 6 do 8 minuta, developeri će mu prestati vjerovati. Opcije:
- splitajte unit i integracijske testove po filename patternima
- vrtite Vitest s CI concurrencyjem gdje je podržano
- držite E2E odvojeno od PR checkova
Ako uvodite širi QA automation program, pogledajte našu kompletnu strategiju testiranja za web i mobile i prilagodite gateove vašoj release kadenci.
# Praktičan primjer: odabir pravog tipa testa#
Kad dodajete feature, odlučite najjeftiniji test koji daje visoko povjerenje.
| Promjena | Najbolji primarni test | Zašto | Sekundarni test |
|---|---|---|---|
| Novo pravilo formatiranja | Unit test | čista logika, brzo | none |
| Nova validacija forme | Unit plus integracija | shema plus UI povezivanje | MSW error test |
| Novo korištenje API endpointa | MSW “contract-ish” test | zaključava request i error handling | mali integracijski test |
| Kritično korisničko putovanje | E2E smoke | end-to-end povjerenje | MSW testovi za edge caseove |
Ovako izbjegavate zamku “sve je integracijski test”, a i dalje isporučujete s pouzdanjem.
# Ključne poruke#
- Izgradite pragmatičnu piramidu: 60–70% unit testova, 25–35% RTL integracijskih testova i 5–10% “contract-ish” API testova uz MSW, plus mali E2E smoke suite.
- Unit testove držite čistima i brzim testiranjem mapiranja, validacije, dozvola i poslovnih pravila izvan React renderiranja.
- React Testing Library koristite za testiranje korisniku vidljivog ponašanja i async UI stanja, a ne implementacijskih detalja ili internih dijelova komponenti.
- MSW koristite s
onUnhandledRequest: 'error'kako biste stabilizirali mrežno ponašanje i rano uhvatili neočekivane API pozive. - Smanjite flakiness uklanjanjem sleepova, resetiranjem stanja između testova te korištenjem
findByiwaitForza async asertacije. - Učinite CI pouzdanim uz fazne gateove, realne coverage pragove i strogo odvajanje brzih PR provjera od sporijih E2E testova.
# Zaključak#
Čvrsta strategija testiranja Reacta u 2026. znači ponovljivu, determinističku povratnu informaciju: Vitest za brzinu, React Testing Library za ponašanje i MSW za stabilna API očekivanja. Kad to spojite s pragmatičnom piramidom i CI gateovima, isporučujete brže, s manje regresija i manje izgubljenog vremena na flaky buildove.
Ako želite da Samioda napravi audit vašeg trenutnog test suitea, skrati CI vrijeme i postavi strategiju prilagođenu vašem React ili Next.js produktu, javite nam se putem naše web stranice i predložit ćemo izvediv plan koji možete implementirati u tjednima, a ne mjesecima.
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 →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.
Observabilnost za Next.js App Router u 2026.: Sentry, OpenTelemetry, traceovi i korisna upozorenja
End-to-end postavke za Next.js logiranje, nadzor i tracing sa Sentryjem i OpenTelemetryjem kroz Server Actions, Route Handlers i razliku Edge naspram Node runtimea—uz dashboarde, pragove upozorenja i korelaciju sesije s traceom.
UX obrasci u Next.js App Routeru: Error Boundaries, Loading UI i streaming kako treba
Praktičan vodič za otporan UX u Next.js App Routeru: struktura route segmenata, error.tsx, loading.tsx, not-found.tsx i Suspense streaming obrasci za djelomično renderiranje, sigurnije dohvaćanje podataka i manje pomaka layouta.
Trebate pomoć s projektom?
Gradimo prilagođena rješenja koristeći tehnologije iz ovog članka. Senior tim, fiksne cijene.
Povezani članci
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.
Kontrolna lista za React code review koju koristimo: performanse, pristupačnost i održivost
Praktična kontrolna lista za React code review usmjerena na performanse, pristupačnost i održivost, s primjerima, savjetima za automatizaciju i copy-paste predloškom.
Kontrolni popis pristupačnosti za React: ARIA, navigacija tipkovnicom, upravljanje fokusom i testiranje (2026.)
Kontrolni popis pristupačnosti za React usmjeren na developere, koji pokriva ARIA, navigaciju tipkovnicom, upravljanje fokusom i automatizirano testiranje s axe i Playwrightom — uz konkretne primjere za forme, modale, izbornike i toast obavijesti.