Web razvoj
ReactTestiranjeVitestReact Testing LibraryMSWCI/CDOsiguranje kvalitete

Strategija testiranja Reacta u 2026.: Vitest + React Testing Library + MSW za sigurne releaseove

AO
Adrijan Omićević
·14 min čitanja

# Š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:

SlojCiljani udioTipično trajanje po testuŠto pokrivaAlati
Unit testovi60–70%1–20msČista logika, mapiranje podataka, validacija, formatiranjeVitest
Integracijski testovi25–35%20–300msPonašanje komponenti, forme, navigacija, async UI stanjaVitest + React Testing Library
“Contract-ish” API testovi5–10%50–500msFrontend očekivanja endpointa, shape errora, caching ponašanjeMSW + Vitest
E2E smoke3–10 kritičnih flowovasekundePlaćanje, signup, checkout, dozvolePlaywright 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:

Bash
npm i -D vitest @vitest/coverage-v8 jsdom \
@testing-library/react @testing-library/jest-dom @testing-library/user-event \
msw

Minimalna Vitest konfiguracija za DOM okruženje:

TypeScript
// 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:

TypeScript
// 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čjePredložena lokacijaNapomena
Unit testovisrc/**/__tests__/*.test.tsDržite blizu koda
Testovi komponentisrc/**/__tests__/*.test.tsxPreferirajte integracijski stil
Shared test utilitest/utils/*Render wrapperi, factoryji
MSW handleritest/msw/handlers.tsCentralno mjesto za API ponašanje
MSW servertest/msw/server.tsJedan 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#

TypeScript
// 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) };
}
TypeScript
// 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čjeZašto je važnoTipični bugovi koje sprječava
Validacijske shemeEdge caseovi s vremenom eksplodirajuNeispravne predaje, broken backfillovi
Pravila cijena/popustaPoslovno kritična logikaBugovi koji utječu na prihode
Provjere dozvolaSigurnost i kontrola pristupaCuranje podataka, pokvarene role
Transformacije podatakaAPI se mijenja, UI očekuje stabilnostGreš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.

TypeScript
// 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.

TSX
// 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:

  1. 1
    Queryajte po role i accessible name, ne po class nameovima.
  2. 2
    Assertajte ono što korisnik vidi, ne interni state.
  3. 3
    Preferirajte findBy za 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 rezultate
  • await waitFor(() => expect(...)) za uvjete
  • izbjegavajte fiksne timeoutove poput setTimeout ili sleep

⚠️ Upozorenje: Izbjegavajte getByText odmah 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.

TypeScript
// test/msw/server.ts
import { setupServer } from 'msw/node';
import { handlers } from './handlers';
 
export const server = setupServer(...handlers);
TypeScript
// 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:

TypeScript
// 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.

TSX
// 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.

ScenarijPreporučeni delayŠto otkriva
Spam klikovi na Save150–300msDupli submitovi, nedostaje disable
Skeleton rendering200–500msLayout shiftovi, nedostaje loading state
Retry/backoff UI300–800msRetry 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. 1
    getByRole s accessible name
  2. 2
    getByLabelText za inpute
  3. 3
    getByText kad predstavlja korisniku vidljiv tekst
  4. 4
    getByTestId samo 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#

SimptomVjerojatan uzrokRješenje
Pada samo na CI-jusporiji CPU, headless razlikekoristite findBy, uklonite sleepove, povećajte default timeoute samo kao zadnju opciju
Pada kad se vrti kao suitetest pollutionresetirajte handlere, resetirajte store, izbjegnite globalne mutacije
Random timing failovirace conditioniawaitajte user eventove, awaitajte async UI, uklonite pogrešnu upotrebu act
“Unhandled request” errorinedostaje MSW handlerdodajte handler ili popravite korištenje endpointa
Failovi vezani uz timezone/datumlocale razlikezamrznite vrijeme u testovima za date logiku

Ako trebate zamrznuti vrijeme za date-sensitive logiku:

TypeScript
// 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#

FazaVrti se naŠto radiTipični budžet
Lint + typechecksvaki PRsprječava očite kvarove1–3 minute
Unit + integracijasvaki PRVitest paralelno2–6 minuta
“Contract-ish” MSW suitesvaki PRmože biti isti run, tagiranouključeno gore
E2E smokemain branch ili nightlysamo kritični flowovi5–15 minuta
Security provjeremain branchprovjere ovisnosti i konfiguracije2–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:

Bash
npx vitest run --coverage

Fail 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.

PromjenaNajbolji primarni testZaštoSekundarni test
Novo pravilo formatiranjaUnit testčista logika, brzonone
Nova validacija formeUnit plus integracijashema plus UI povezivanjeMSW error test
Novo korištenje API endpointaMSW “contract-ish” testzaključava request i error handlingmali integracijski test
Kritično korisničko putovanjeE2E smokeend-to-end povjerenjeMSW 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 findBy i waitFor za 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

Share
A
Adrijan OmićevićOsnivač i senior developer

Osnivač i senior developer u Samiodi. 8+ godina iskustva u izradi React, Next.js, Flutter i n8n rješenja za klijente diljem Europe.

Trebate pomoć s projektom?

Gradimo prilagođena rješenja koristeći tehnologije iz ovog članka. Senior tim, fiksne cijene.