11.17-bob: Testing — Vitest, React Testing Library, Playwright
11-QISM — Frontend: React · 17-mavzu
1. Kirish va motivatsiya
11-QISM davomida React ilovasini qurishni to'liq o'rgandik: komponent, state, hooklar, routing, forma, performance, animatsiya. Ilovamiz funksional va chiroyli. Lekin u ishonchlimi? Siz yangi feature qo'shganda eski narsa buzilmaganini qaerdan bilasiz? Kodni refactoring qilganda (toza qilganda) hech narsani sindirmaganingizga ishonchingiz bormi? Mahsulot foydalanuvchilar qo'lida kutilgandek ishlashiga kafolat beruvchi narsa — bu test. 11-QISM'ning yakuniy bobi aynan shu — kodga ishonch beruvchi qatlam.
Test — bu "kodni sinab ko'rish"ning avtomatlashtirilgan versiyasi. Qo'lda sinash (har o'zgarishdan keyin ilovani ochib, har tugmani bosib ko'rish) — sekin, zerikarli, va ishonchsiz (biror narsani unutasiz). Avtomatlashtirilgan test — bir marta yoziladi, keyin har o'zgarishda (yoki CI/CD'da — 10.5) avtomatik ishlaydi va "hammasi joyidami?"ni soniyalarda aytadi. Bu — ishonch beradi: refactoring qilasiz (test o'tsa — buzilmagan), yangi feature qo'shasiz (eski testlar o'tsa — regression yo'q), va xato (bug) qayta paydo bo'lmasligini kafolatlaysiz (bug uchun test yozasiz — keyin u qayta kelsa, test darrov tutadi).
Bu bob: nega test (ishonch, refactoring, regression), test turlari (unit, integration, e2e — testing trophy), Vitest (zamonaviy, tez test runner — Jest muqobili), test anatomiyasi (describe/it/expect), React Testing Library falsafasi (foydalanuvchi nuqtai nazaridan — implementatsiya emas), render/screen/queries (getBy/findBy/queryBy), foydalanuvchi interaksiyasi (userEvent), async test (findBy/waitFor), mocking (vi.fn/vi.mock/MSW), custom hook test (renderHook), Playwright (e2e — butun ilovani real brauzerda), va test strategiyasi (nima test qilish, coverage). Bu mavzular to'liq va amaliy misollar bilan ochiladi.
O'xshatish: Test — bu bino qabul komissiyasi va xavfsizlik tekshiruvi. Siz bino qurdingiz (kod yozdingiz) — lekin u xavfsizmi? Liftlar ishlaydimi? Eshiklar ochiladimi? Yong'in chiqsa nima bo'ladi? Qo'lda tekshirish — har xonaga kirib, har tugmani bosib ko'rish (sekin, biror narsani unutasiz). Avtomatik test — bu doimiy nazoratchi robot: har o'zgarishdan keyin butun binoni soniyalarda tekshiradi ("lift ishlaydi , eshik ochiladi , yong'in signali ishlaydi "). Unit test — bitta g'isht (funksiya/komponent) mustahkammi? Integration test — g'ishtlar birga to'g'ri ishlaydimi (forma to'ldirib yuborish)? E2e test — mehmon eshikdan kirib, liftga chiqib, xonaga yetib bora oladimi (butun yo'l — login'dan to'lovgacha)? Test'siz bino — har o'zgarishda "qulamaydimi?" deb qo'rqib yashashingiz kerak; test bilan — ishonch.
Nega muhim?
- Ishonch — test bilan refactoring/yangi feature qo'rqmasdan qilinadi (eski testlar buzilishni tutadi).
- Regression — bir marta tuzatilgan bug qayta kelsa, test darrov tutadi (qayta buzilmaydi).
- Hujjat — test — kodning "qanday ishlashi kerak"ligini ko'rsatadi (jonli misol).
- Professional standart — jiddiy loyiha test'siz bo'lmaydi; CI/CD 10.5-bob test'ga tayanadi.
2. Nazariya — chuqur tushuntirish
2.1. Nega test va test turlari
TEST — kodni AVTOMATIK sinash (qo'lda har safar tekshirish o'rniga):
NEGA:
Ishonch — refactoring/yangi feature (eski test o'tsa — buzilmagan)
Regression — tuzatilgan bug qayta kelsa — test darrov tutadi
Hujjat — test kodning qanday ishlashini ko'rsatadi (jonli)
CI/CD — har push'da avtomatik tekshirish 10.5-bob
TEST TURLARI (Testing Trophy — Kent C. Dodds):
┌─────────────────────────────────────────┐
│ E2E (kam) — butun ilova (real brauzer — Playwright) │
│ INTEGRATION (ko'p)— komponentlar birga (RTL — ASOSIY) │
│ UNIT (o'rta) — funksiya/hook (Vitest) │
│ STATIC — TypeScript, ESLint (xato yozmasdan) │
└─────────────────────────────────────────┘
Integration test eng ko'p qiymat (komponentlar BIRGA — foydalanuvchi kabi)
E2e kam, lekin muhim (kritik yo'l — login, to'lov); unit — murakkab mantiqNega test va test turlari — test strategiyasining asosi. Nega test: ishonch (refactoring/yangi feature — eski testlar o'tsa buzilmagan), regression (tuzatilgan bug qayta kelsa test tutadi), hujjat (test kodning qanday ishlashini ko'rsatadi — jonli misol), CI/CD (har push'da avtomatik — 10.5). Test turlari (Kent C. Dodds'ning "Testing Trophy" — eski "piramida"ning zamonaviy versiyasi): static (poydevor — TypeScript, ESLint — xato kod yozishni oldini oladi — 11.14, 15.3); unit (alohida funksiya/hook — Vitest); integration (komponentlar birga — React Testing Library — eng ko'p qiymat); e2e (butun ilova real brauzerda — Playwright — kam, lekin muhim). Ikki tamoyil: (1) integration test eng ko'p qiymat beradi — u komponentlarni birga, foydalanuvchi kabi sinaydi (real ishlatish holatiga yaqin); (2) e2e kam (sekin, qimmat — faqat kritik yo'llar: login, to'lov, asosiy oqim), unit esa murakkab mantiq uchun (hisob-kitob, custom hook). To'g'ri balans — ko'p integration, oz e2e, kerakli unit. Bu — samarali test strategiyasi (har joyni 100% emas, muhim joylarni ishonchli).
2.2. Vitest — setup va test anatomiyasi
VITEST — zamonaviy, tez test runner (Vite uchun — Jest muqobili):
npm install -D vitest @testing-library/react @testing-library/user-event jsdom
// vite.config.ts — test sozlamasi:
test: { environment: "jsdom", globals: true, setupFiles: "./src/test/setup.ts" }
// jsdom — brauzer muhiti (DOM); globals — describe/it/expect global
TEST FAYLI: Component.test.tsx (yoki .spec.tsx)
TEST ANATOMIYASI:
import { describe, it, expect } from "vitest";
describe("sum funksiyasi", () => { // guruh (tavsif)
it("ikki sonni qo'shadi", () => { // bitta test (holat)
const result = sum(2, 3); // ARRANGE + ACT
expect(result).toBe(5); // ASSERT (tekshiruv)
});
});
AAA NAQSHI: Arrange (tayyorla) Act (bajar) Assert (tekshir)
Vitest — Vite bilan tez, Jest API'si bilan mos (describe/it/expect — bir xil)
describe (guruh) > it (test) > expect (tekshiruv) — AAA naqshi (tayyor/bajar/tekshir)Vitest — setup va test anatomiyasi — test yozishning asosi. Vitest — Vite uchun zamonaviy, tez test runner (Jest'ning muqobili, lekin Vite bilan integratsiyalangan va tezroq). O'rnatish:
npm install -D vitest @testing-library/react @testing-library/user-event jsdom, vavite.config'da test sozlamasi (environment: "jsdom"— brauzer DOM muhiti,globals: true—describe/it/expectni global qiladi). Test fayllari*.test.tsx(yoki.spec.tsx). Test anatomiyasi:describe("guruh", () => { it("holat", () => { ...; expect(...).toBe(...) }) })—describe(testlar guruhi/tavsif),it(bitta test — bitta holat),expect(tekshiruv —toBe,toEqual,toContain...). AAA naqshi: Arrange (tayyorlash — ma'lumot/komponent), Act (bajarish — funksiyani chaqirish/tugma bosish), Assert (tekshirish — natija kutilgandekmi). Ikki nuqta: (1) Vitest — Vite bilan tez va Jest API'siga mos (describe/it/expect— bir xil sintaksis, Jest'dan o'tish oson); (2) AAA naqshi (Arrange-Act-Assert) — har testni tushunarli, izchil qiladi (avval tayyorla, keyin bajar, oxirida tekshir). Bu — barcha test turlarining umumiy strukturasi.
2.3. React Testing Library falsafasi
RTL FALSAFASI: "foydalanuvchi kabi sina" (implementatsiya emas, XULQ):
IMPLEMENTATSIYA test (komponent ICHINI tekshirish — mo'rt):
expect(component.state.count).toBe(5); // ichki state — refactoring'da buziladi
// state nomini o'zgartirsang test buziladi (lekin UI to'g'ri — yolg'on xato)
XULQ test (foydalanuvchi KO'RADIGANINI tekshirish — ishonchli):
expect(screen.getByText("Hisob: 5")).toBeInTheDocument(); // foydalanuvchi ko'radigan
// ichki qanday qilinsa ham, EKRANda 5 ko'rinsa — test o'tadi (refactoring xavfsiz)
RTL TAMOYILI: "Testlar ilova ishlatilishiga qancha o'xshasa, shuncha ishonch beradi"
- foydalanuvchi matnni ko'radi, tugmani bosadi, formani to'ldiradi — TEST ham SHUNDAY
- komponent ichki tuzilishi (state, props, metod) — TEST BILMASIN (implementatsiya)
RTL — foydalanuvchi nuqtai nazaridan (ko'radigan, bosadigan); ichki tuzilishni emas
Bu refactoring'ni XAVFSIZ qiladi (ichni o'zgartirsang ham — UI to'g'ri bo'lsa test o'tadi)React Testing Library falsafasi — RTL'ning eng muhim tushunchasi (boshqa test kutubxonalardan farqi). Asosiy tamoyil — "foydalanuvchi kabi sina": komponentni uning ichki tuzilishi (state, props, metodlar) orqali emas, foydalanuvchi ko'radigan va qiladigan narsalar orqali sinash. Implementatsiya testi (yomon):
expect(component.state.count).toBe(5)— ichkistateni tekshiradi, mo'rt (refactoring'da buziladi —statenomini o'zgartirsangiz test buziladi, garchi UI to'g'ri bo'lsa ham — yolg'on xato). Xulq testi (yaxshi):expect(screen.getByText("Hisob: 5")).toBeInTheDocument()— foydalanuvchi ko'radigan narsani tekshiradi (ichki qanday qilinsa ham, ekranda "5" ko'rinsa test o'tadi). RTL'ning mashhur tamoyili: "Testlar ilova ishlatilishiga qancha o'xshasa, shuncha ishonch beradi" — foydalanuvchi matnni ko'radi, tugmani bosadi, formani to'ldiradi test ham shu tarzda (ichki tuzilishni bilmasin). Ikki nuqta: (1) RTL — foydalanuvchi nuqtai nazaridan (ko'radigan, bosadigan — implementatsiya emas); (2) bu refactoring'ni xavfsiz qiladi (kod ichini o'zgartirsangiz ham, UI bir xil ishlasa test o'tadi — bu testlarning asosiy maqsadi: ishonch berish, to'sqinlik qilmaslik). Bu falsafa — RTL'ni zamonaviy React test standartiga aylantirdi.
2.4. render, screen va queries
render — komponentni test muhitiga chizadi; screen — undan element topadi:
import { render, screen } from "@testing-library/react";
render(<Button text="Saqlash" />); // komponentni chiz
const btn = screen.getByText("Saqlash"); // undan element top
QUERY TURLARI (element topish — 3 oila):
getBy* — element BOR deb kutadi (yo'q bo'lsa — XATO darrov) — sinxron, eng keng
queryBy* — element bo'lmasligi mumkin (yo'q bo'lsa — null) — "yo'qligini" tekshirish
findBy* — element KEYINROQ paydo bo'ladi (async — Promise) — kutadi 2.6-bob
QUERY USULLARI (qaysi bo'yicha topish — PRIORITET tartibda):
getByRole — ARIA roli (button, heading, textbox) — ENG YAXSHI (a11y — 1.9)
getByLabelText — label (forma maydoni)
getByText — matn
getByPlaceholderText, getByTestId (oxirgi chora — data-testid)
getByRole afzal (foydalanuvchi/ekran o'quvchi kabi — a11y'ni ham tekshiradi)
getBy (bor) | queryBy (yo'qligini) | findBy (async kutadi — 2.6)render, screen va queries — komponentni sinashning asosiy vositalari.
render(<Component />)— komponentni test muhitiga (jsdom) chizadi;screen— chizilgan komponentdan elementlarni topadi. Query turlari (uch oila — qachon qaysi):getBy*(element bor deb kutadi — yo'q bo'lsa darrov xato — sinxron, eng keng ishlatiladigan),queryBy*(element bo'lmasligi mumkin — yo'q bo'lsanullqaytaradi — "element yo'qligini" tekshirish uchun),findBy*(element keyinroq paydo bo'ladi — async, Promise qaytaradi, kutadi — 2.6). Query usullari (qaysi bo'yicha topish — prioritet tartibda):getByRole(ARIA roli —button,heading,textbox— eng yaxshi, chunki foydalanuvchi/ekran o'quvchi kabi va a11y'ni ham tekshiradi — 1.9),getByLabelText(forma maydoni — label bo'yicha),getByText(matn),getByPlaceholderText, vagetByTestId(data-testid— oxirgi chora, faqat boshqa usul yo'q bo'lsa). Ikki nuqta: (1)getByRoleafzal (foydalanuvchi va ekran o'quvchi kabi, va a11y to'g'riligini ham tekshiradi — yaxshi test ham yaxshi a11y'ni rag'batlantiradi); (2)getBy(element bor),queryBy(yo'qligini tekshirish),findBy(async kutadi — 2.6) — har birining o'z vazifasi. Bu — element topishning to'g'ri usuli.
2.5. userEvent — foydalanuvchi interaksiyasi
userEvent — foydalanuvchi harakatini TAQLID qiladi (haqiqiy click/type kabi):
import userEvent from "@testing-library/user-event";
it("tugma bosilganda hisob oshadi", async () => {
const user = userEvent.setup(); // userEvent boshla
render(<Counter />);
const btn = screen.getByRole("button", { name: "+" });
await user.click(btn); // async (haqiqiy click kabi)
expect(screen.getByText("Hisob: 1")).toBeInTheDocument();
});
userEvent USULLARI:
user.click(el) — bosish
user.type(input, "text")— yozish (har harf alohida — haqiqiy kabi)
user.clear(input) — tozalash
user.selectOptions(select, "value") — tanlash
user.keyboard("{Enter}")— klaviatura
fireEvent (eski/past daraja) vs userEvent (yangi — haqiqiyroq):
userEvent afzal (haqiqiy foydalanuvchi kabi — fokus, hover, har harf)
userEvent — haqiqiy foydalanuvchi harakatini taqlid (await — async)
userEvent.setup() bilan boshla; har amal — await (haqiqiy vaqt kabi)
userEvent— foydalanuvchi harakatini (click, type, select) haqiqiy taqlid qiluvchi kutubxona. Ishlatish:const user = userEvent.setup()(boshlash), keyinawait user.click(btn),await user.type(input, "text")— har amalawaitbilan (chunki userEvent haqiqiy foydalanuvchi kabi vaqt oladi — har harf alohida yoziladi, fokus o'zgaradi). Misol: tugmani topib (getByRole("button", { name: "+" })),await user.click(btn), keyin natijani tekshirish (expect(screen.getByText("Hisob: 1"))). userEvent usullari:click(bosish),type(yozish — har harf alohida, haqiqiy kabi),clear(tozalash),selectOptions(tanlash),keyboard("{Enter}")(klaviatura).fireEventvsuserEvent:fireEvent— eski, past daraja (bitta DOM event),userEvent— yangi, haqiqiyroq (fokus, hover, har harf alohida — real foydalanuvchi kabi). Ikki nuqta: (1)userEventafzal (fireEvent'dan) — u haqiqiy foydalanuvchi xatti-harakatini taqlid qiladi (har harf type, fokus, hover — real test); (2)userEvent.setup()bilan boshla va har amalniawaitqil (haqiqiy vaqt kabi — async). Bu — interaktiv komponentlarni (forma, tugma, hisoblagich) foydalanuvchi nuqtai nazaridan sinashning standarti.
2.6. Async test — findBy va waitFor
ASYNC — ma'lumot KEYINROQ paydo bo'ladi (fetch, setTimeout) kutish kerak:
// findBy* — element KELANGUNCHA kutadi (async query — Promise):
it("ma'lumot yuklangach ko'rsatadi", async () => {
render(<UserList />); // fetch boshlanadi (yuklanmoqda)
// findBy — element paydo bo'lguncha kutadi (max 1s default)
const user = await screen.findByText("Ali"); // ma'lumot kelgach "Ali" paydo bo'ladi
expect(user).toBeInTheDocument();
});
// waitFor — biror shart bajarilguncha kutadi (murakkabroq holat):
await waitFor(() => {
expect(screen.getByText("Yuklandi")).toBeInTheDocument();
});
// Yo'qolishni kutish (loading tugashi):
await waitForElementToBeRemoved(() => screen.queryByText("Yuklanmoqda..."));
┌────────────────────────────────────────────────────────────┐
│ findBy: element paydo bo'lishini kutadi | waitFor: shart │
│ getBy: darrov (sinxron) | findBy: async (kutadi) │
└────────────────────────────────────────────────────────────┘
Async ma'lumot (fetch) findBy (kutadi); getBy DARROV (yo'q bo'lsa xato)
waitFor — murakkab shart kutish; findByText — element paydo bo'lishi (eng keng)Async test — findBy va waitFor — keyinroq paydo bo'ladigan ma'lumotni sinash. Muammo: komponent serverdan ma'lumot olsa (fetch — 11.5), u darrov ekranda yo'q (avval "yuklanmoqda", keyin ma'lumot) —
getBy*(sinxron — darrov tekshiradi) xato beradi (element hali yo'q). Yechim — async query'lar:findBy*— element paydo bo'lguncha kutadi (Promise qaytaradi, default 1 soniya):const user = await screen.findByText("Ali")(ma'lumot kelgach "Ali" paydo bo'ladi, findBy uni kutadi).waitFor— murakkabroq shart bajarilguncha kutadi:await waitFor(() => expect(screen.getByText("Yuklandi")).toBeInTheDocument()).waitForElementToBeRemoved— element yo'qolishini kutadi (loading spinner tugashi —await waitForElementToBeRemoved(() => screen.queryByText("Yuklanmoqda..."))). Ikki nuqta: (1) async ma'lumot (fetch, taymer)findBy(element paydo bo'lishini kutadi);getByfaqat darrov mavjud element uchun (yo'q bo'lsa xato); (2)waitFor— murakkab shart (bir nechta yoki maxsus holat),findByText— element paydo bo'lishi (eng keng ishlatiladigan). Bu — real (serverga ulangan) komponentlarni sinashning zarur qismi (deyarli har komponent ma'lumot oladi).
2.7. Mocking — vi.fn, vi.mock va MSW
MOCK — haqiqiy bog'liqlikni SOXTA bilan almashtirish (test izolyatsiya + nazorat):
// 1. vi.fn() — soxta funksiya (chaqirilganini tekshirish):
const onClick = vi.fn(); // soxta funksiya
render(<Button onClick={onClick} />);
await user.click(screen.getByRole("button"));
expect(onClick).toHaveBeenCalledTimes(1); // chaqirildimi? (callback test)
// 2. vi.mock() — butun modulni mock (masalan API moduli):
vi.mock("./api", () => ({ getUser: vi.fn(() => Promise.resolve({ name: "Ali" })) }));
// 3. MSW (Mock Service Worker) — TARMOQ darajasida mock (eng yaxshi — haqiqiyroq):
// server.use(http.get("/api/users", () => HttpResponse.json([{ name: "Ali" }])));
// komponent haqiqiy fetch qiladi, lekin MSW soxta javob beradi (real kabi)
┌────────────────────────────────────────────────────────────┐
│ vi.fn(): callback/funksiya | vi.mock(): modul | MSW: tarmoq │
│ MSW eng yaxshi (haqiqiy fetch + soxta javob — real kabi) │
└────────────────────────────────────────────────────────────┘
Mock — bog'liqlikni (API, funksiya) soxta bilan (test izolyatsiya, tez, nazorat)
MSW — API mock'ning eng yaxshi usuli (tarmoq darajasida — komponent haqiqiy so'rov)Mocking — haqiqiy bog'liqlikni (API, funksiya, modul) soxta (mock) bilan almashtirish (test izolyatsiya, tezlik, nazorat uchun). Uch usul: (1)
vi.fn()— soxta funksiya (callback test):const onClick = vi.fn()<Button onClick={onClick} />clickexpect(onClick).toHaveBeenCalledTimes(1)(funksiya chaqirildimi, qanday argument bilan — tekshiriladi). (2)vi.mock()— butun modulni mock (masalan API moduli — haqiqiy serverga bormasin):vi.mock("./api", () => ({ getUser: vi.fn(() => Promise.resolve({ name: "Ali" })) })). (3) MSW (Mock Service Worker) — tarmoq darajasida mock (eng yaxshi usul): komponent haqiqiyfetchqiladi, lekin MSW so'rovni "ushlab", soxta javob beradi (real kabi — komponent kodini o'zgartirish kerak emas). Ikki nuqta: (1) mock — bog'liqlikni soxta bilan almashtiradi (test izolyatsiyalangan, tez — haqiqiy serverga bog'liq emas, va nazoratli — soxta javobni siz belgilaysiz); (2) MSW — API mock'ning eng yaxshi usuli (tarmoq darajasida — komponent haqiqiy so'rov yuboradi, MSW javob beradi — bu eng "haqiqiy" va ishonchli).vi.fncallback'lar uchun, MSW API'lar uchun ideal. Mocking — komponentni tashqi bog'liqliksiz, tez va ishonchli sinashning kaliti.
2.8. Custom hook test — renderHook
CUSTOM HOOK 11.7-bob — komponentsiz sinash (renderHook):
import { renderHook, act } from "@testing-library/react";
it("useCounter oshadi", () => {
const { result } = renderHook(() => useCounter(0)); // hook'ni "render" qiladi
expect(result.current.count).toBe(0); // boshlang'ich qiymat
act(() => { // state o'zgarishni act bilan o'ra
result.current.increment(); // hook funksiyasini chaqir
});
expect(result.current.count).toBe(1); // o'zgardimi?
});
- renderHook(() => useX()) — hook'ni komponentsiz ishga tushiradi
- result.current — hook qaytargan joriy qiymat
- act() — state o'zgarishni o'raydi (React ogohlantirishini oldini oladi)
renderHook — custom hook'ni komponentsiz sinash (mantiq izolyatsiya — 11.7)
act() — state o'zgartiruvchi amalni o'ra (re-render to'g'ri bo'lsin)Custom hook test — renderHook — custom hook'larni 11.7-bob komponentsiz sinash.
renderHook— hook'ni alohida (komponentga solmasdan) ishga tushiradi:const { result } = renderHook(() => useCounter(0)).result.current— hook qaytargan joriy qiymat (result.current.count,result.current.increment). State o'zgartiruvchi amalniact()bilan o'rash kerak:act(() => { result.current.increment() })— bu React'ga state o'zgarishi va re-render to'g'ri bo'lishini ta'minlaydi (act'siz React ogohlantiradi). Naqsh: boshlang'ich qiymatni tekshiractichida hook funksiyasini chaqir yangi qiymatni tekshir. Ikki nuqta: (1)renderHook— custom hook'ni komponentsiz sinash (hook'ning mantig'i UI'dan ajralgan — 11.7: 2.14 — shuning uchun alohida, oson sinaladi); (2)act()— state o'zgartiruvchi amallarni o'rash (increment,setState— re-render to'g'ri kuzatilsin). Bu — custom hook'larni ishonchli sinashning standarti (hook — mantiq, mantiqni alohida sinash — toza, fokuslangan test). Murakkab hook'lar (useFetch, useDebounce) uchun ayniqsa foydali.
2.9. Playwright — e2e setup va test
PLAYWRIGHT — E2E (end-to-end): butun ilovani REAL brauzerda sinash:
npm init playwright@latest
E2E — foydalanuvchi KABI butun OQIMni sinash (real brauzer, real navigatsiya):
import { test, expect } from "@playwright/test";
test("foydalanuvchi login qiladi", async ({ page }) => {
await page.goto("/login"); // sahifaga bor
await page.getByLabel("Email").fill("ali@x.uz"); // formani to'ldir
await page.getByLabel("Parol").fill("12345678");
await page.getByRole("button", { name: "Kirish" }).click(); // tugma bos
await expect(page).toHaveURL("/dashboard"); // dashboard'ga o'tdimi?
await expect(page.getByText("Xush kelibsiz")).toBeVisible();
});
RTL vs PLAYWRIGHT:
- RTL (integration) — jsdom (soxta brauzer), bitta komponent/sahifa (tez)
- Playwright (e2e) — REAL brauzer (Chrome/Firefox/Safari), butun ilova (sekinroq)
Playwright — butun oqim (logindashboardamal) real brauzerda (kritik yo'l)
E2e KAM (sekin, qimmat) — faqat kritik oqim (login, to'lov, asosiy yo'l — 2.1)Playwright — e2e — butun ilovani real brauzerda sinash (end-to-end). O'rnatish:
npm init playwright@latest. E2e — foydalanuvchi kabi butun oqimni sinaydi (real brauzer — Chrome/Firefox/Safari, real navigatsiya, real klick):test("login", async ({ page }) => { await page.goto("/login"); await page.getByLabel("Email").fill(...); await page.getByRole("button", { name: "Kirish" }).click(); await expect(page).toHaveURL("/dashboard") })— foydalanuvchi login sahifasiga boradi, formani to'ldiradi, tugmani bosadi, va haqiqatan dashboard'ga o'tganini tekshiradi. RTL vs Playwright: RTL (integration) — jsdom (soxta brauzer), bitta komponent/sahifa, tez; Playwright (e2e) — real brauzer, butun ilova (login'dan to'lovgacha — to'liq oqim), sekinroq lekin eng ishonchli. Ikki nuqta: (1) Playwright — butun oqimni (login dashboard amal — bir necha sahifa orqali) real brauzerda sinaydi (haqiqiy foydalanuvchi tajribasiga eng yaqin); (2) e2e kam ishlatiladi (sekin, qimmat, mo'rt) — faqat kritik yo'llar (login, to'lov, asosiy xarid oqimi — 2.1 testing trophy). To'g'ri balans — ko'p RTL integration test + bir nechta kritik Playwright e2e test. Playwright — eng yuqori ishonch darajasini beradi (real brauzer, real oqim).
2.10. Test strategiyasi — nima test qilish
NIMA TEST QILISH (har joyni emas — MUHIM joylarni):
TEST QIL:
- Murakkab mantiq (hisob-kitob, validatsiya, custom hook — 2.8)
- Foydalanuvchi oqimlari (forma yuborish, login, qidiruv)
- Kritik yo'llar (to'lov, ro'yxatdan o'tish — e2e — 2.9)
- Tuzatilgan bug (regression — qayta kelmasin)
- Shartli mantiq (turli holatlar — empty/error/data — 11.15: 2.8)
TEST QILMA (kam qiymat):
- Oddiy ko'rsatish (faqat props chizadigan komponent)
- Tashqi kutubxona (React/Swiper — ular o'zi sinalgan)
- Implementatsiya tafsiloti (ichki state — 2.3)
COVERAGE (qamrov) — qancha kod test qilingan (%):
- 100% maqsad EMAS (foydasiz testlar — vaqt isrofi)
- MUHIM kod (mantiq, oqim) yaxshi qamralsin (sifat > raqam)
Test — MUHIM joylarga (mantiq, oqim, bug); har qatorga emas (sifat > coverage raqami)
Bug topilsa — avval TEST yoz (qayta kelmasin), keyin tuzat (TDD ruhi)Test strategiyasi — nima test qilish — testlarni qaerga yo'naltirish (har joyni 100% emas, muhim joylarni). Test qilish kerak: murakkab mantiq (hisob-kitob, validatsiya, custom hook — 2.8), foydalanuvchi oqimlari (forma yuborish, login, qidiruv), kritik yo'llar (to'lov, ro'yxatdan o'tish — e2e — 2.9), tuzatilgan bug (regression — qayta kelmasligini kafolatlash), shartli mantiq (empty/error/data holatlari — 11.15: 2.8). Test qilmaslik (kam qiymat): oddiy ko'rsatish (faqat props chizadigan komponent — sinashga arzimadi), tashqi kutubxona (React, Swiper — ular o'zi sinalgan), implementatsiya tafsiloti (ichki state — 2.3). Coverage (qamrov — qancha kod test qilingan %): 100% maqsad emas (foydasiz testlar vaqt isrofi va mo'rt) — muhim kod (mantiq, oqim) yaxshi qamralsin (sifat > raqam). Ikki tamoyil: (1) test — muhim joylarga (mantiq, foydalanuvchi oqimi, bug); har qatorga emas (coverage raqamini quvmaslik — sifat muhim); (2) bug topilsa — avval uni qaytaradigan test yoz (regression — qayta kelmasin), keyin tuzat (TDD — Test-Driven Development ruhi). Bu — samarali, qiymatli test strategiyasi (kam, lekin to'g'ri test > ko'p, lekin foydasiz test).
2.11. Test hayotiy sikli — hook, spyOn, fake timer va coverage
TEST HOOK'LARI (setup/teardown — har test toza boshlansin):
beforeEach(() => { ... }) — HAR test'dan OLDIN (tayyorlash — masalan mock reset)
afterEach(() => { ... }) — HAR test'dan KEYIN (tozalash)
beforeAll / afterAll — bir marta (guruh boshi/oxiri — MSW server — Misol 8)
MOCK VOSITALARI:
vi.fn() — soxta funksiya (callback — 2.7)
vi.spyOn(obj, "metod") — MAVJUD metodni kuzatish (asl saqlanadi yoki mock qilinadi)
vi.mock("./modul") — butun modul 2.7-bob
vi.clearAllMocks() — chaqiruv tarixini tozalash (beforeEach ichida — izolyatsiya)
// spyOn misoli — console.error chaqirilganini tekshirish (aslini bosmasdan):
const spy = vi.spyOn(console, "error").mockImplementation(() => {});
// ... test ...
expect(spy).toHaveBeenCalled();
FAKE TIMER — vaqtni "tezlashtirish" (setTimeout/debounce testi — real kutmasdan):
vi.useFakeTimers();
render(<Debounced />);
vi.advanceTimersByTime(500); // 500ms "o'tdi" (real 500ms kutmaymiz)
vi.useRealTimers(); // oxirida qaytar
COVERAGE (qamrov — c8 yoki istanbul provayder):
// vite.config: test.coverage = { provider: "v8", reporter: ["text","html"] }
npm run test -- --coverage // qaysi qatorlar test qilingan — hisobot
beforeEach + clearAllMocks — har test IZOLYATSIYA (biri boshqasiga ta'sir qilmasin)
Fake timer — taymer/debounce testini TEZ va DETERMINISTIK qiladi (real vaqt kutmasdan)Test hayotiy sikli — hook, spyOn, fake timer va coverage — testlarni toza, izolyatsiyalangan va determinlashgan qiluvchi vositalar. Setup/teardown hook'lari:
beforeEach(har test'dan oldin — masalanvi.clearAllMocks()bilan mock tarixini tozalash),afterEach(har test'dan keyin — tozalash; RTL DOM'ni avtomatik tozalaydi),beforeAll/afterAll(guruh boshi/oxirida bir marta — MSW serverni ishga tushirish/to'xtatish — Misol 8). Mock vositalari:vi.fn()(soxta funksiya — 2.7),vi.spyOn(obj, "metod")(mavjud metodni kuzatish — chaqirildimi, qanday argument bilan; asl xulqni saqlash yokimockImplementationbilan almashtirish mumkin — masalanconsole.errorni bosmasdan tekshirish),vi.mock()(butun modul),vi.clearAllMocks()(chaqiruv tarixini tozalash —beforeEachda izolyatsiya uchun). Fake timer — vaqtga bog'liq kodni (debounce,setTimeout, polling — 11.7) real kutmasdan sinash:vi.useFakeTimers()vi.advanceTimersByTime(500)(500ms "o'tkazadi")vi.useRealTimers()(oxirida qaytarish). Coverage (qamrov) — Vitestv8(c8) yokiistanbulprovayder bilan qaysi qatorlar test qilinganini hisoblaydi (--coveragebayrog'i,reporter: ["text", "html"]). Ikki nuqta: (1)beforeEach+vi.clearAllMocks()har testni izolyatsiya qiladi (biri ikkinchisiga ta'sir qilmasin — flaky testlarning oldini oladi — Xato 5); (2) fake timer taymer/debounce testini tez va deterministik qiladi (real vaqt kutish — sekin va beqaror). Bu vositalar — ishonchli, takrorlanadigan test poydevorini quradi.
2.12. Snapshot test va router test
SNAPSHOT TEST — komponent chiqishini "suratga olib" saqlaydi, keyin taqqoslaydi:
expect(container).toMatchSnapshot(); // 1-ishga tushirish: __snapshots__ ga yozadi
// keyingi ishlar: chiqish o'zgarsa — test yiqiladi (diff ko'rsatiladi)
QACHON FOYDALI: barqaror, murakkab chiqish (masalan formatlangan matn, config)
QACHON ZARARLI: tez-tez o'zgaradigan UI (har o'zgarishda snapshot yangilanadi
"u --update" refleks bilan bosiladi test qiymatini yo'qotadi)
Snapshot — EHTIYOTKORLIK bilan (katta UI snapshot'i mo'rt — xulq testi afzal — 2.3)
ROUTER TEST — routing'ga bog'liq komponent 11.9-bob MemoryRouter bilan:
import { MemoryRouter } from "react-router-dom";
it("URL'ga qarab to'g'ri sahifa ko'rinadi", () => {
render(
<MemoryRouter initialEntries={["/users/5"]}> // boshlang'ich URL
<Routes><Route path="/users/:id" element={<UserPage />} /></Routes>
</MemoryRouter>
);
expect(screen.getByText(/foydalanuvchi 5/i)).toBeInTheDocument();
});
MemoryRouter — brauzersiz (xotirada) routing test (initialEntries — boshlang'ich yo'l)Snapshot test va router test — ikkita qo'shimcha, lekin muhim texnika. Snapshot test — komponentning render chiqishini birinchi ishga tushishda "surat" qilib (
__snapshots__papkasiga) saqlaydi, keyingi ishlarda joriy chiqishni saqlangan bilan taqqoslaydi:expect(container).toMatchSnapshot(). Agar chiqish o'zgarsa — test yiqiladi va farq (diff) ko'rsatiladi. Qachon foydali: barqaror va murakkab chiqish (formatlangan matn, konfiguratsiya obyekti, kichik saf komponent). Qachon zararli: tez-tez o'zgaradigan UI — har o'zgarishda snapshot "yangilanadi" (--update), dasturchi diff'ni o'qimasdan reflektorik tasdiqlaydi test o'z qiymatini yo'qotadi (yolg'on xotirjamlik). Shuning uchun snapshot ehtiyotkorlik bilan — katta UI snapshot'i mo'rt; ko'p holatda aniq xulq testi (getByText,getByRole— 2.3) afzal. Router test — routing'ga bog'liq komponentni (useParams,Link,useNavigate— 11.9)MemoryRouterbilan sinash: u brauzersiz, xotirada routing beradi,initialEntries={["/users/5"]}bilan boshlang'ich URL belgilanadi. Misolda/users/:idyo'liga mos komponent to'g'ri sahifani ko'rsatishi tekshiriladi.MemoryRouter— brauzer manzil satriga bog'liq bo'lmagan, tez va izolyatsiyalangan routing test'ini beradi (real brauzer navigatsiyasi — Playwright e2e ishi — 2.9).
2.13. Yordamchi query'lar — within, rerender, cleanup; a11y test va TDD
QO'SHIMCHA RTL VOSITALARI:
within(el) — element ICHIDA qidirish (masalan bitta ro'yxat elementi ichida)
const row = screen.getByRole("row", { name: /Ali/ });
within(row).getByRole("button", { name: "O'chirish" }); // shu QATOR ichida
const { rerender } = render(<Badge count={1} />);
rerender(<Badge count={2} />); // BOSHQA props bilan qayta render (props o'zgarishi testi)
cleanup() — DOM'ni tozalash (RTL afterEach'da AVTOMATIK — globals:true — 2.2)
A11Y TEST (jest-axe) — avtomatik accessibility tekshiruvi:
import { axe } from "jest-axe";
const { container } = render(<Form />);
expect(await axe(container)).toHaveNoViolations(); // a11y buzilishi bormi?
TDD (Test-Driven Development) — avval TEST, keyin KOD:
1. Red — test yoz (hali kod yo'q — yiqiladi)
2. Green — minimal kod yoz (test o'tsin)
3. Refactor — kodni tozala (test yashil qoladi — ishonch)
within — chegaralangan qidiruv (bir nechta o'xshash element orasidan aniq topish)
jest-axe — a11y'ni AVTOMATIK tekshiradi (getByRole a11y'ni rag'batlantiradi — 2.4)Yordamchi query'lar, a11y test va TDD — testni aniq va sifatli qiluvchi vositalar.
within(element)— berilgan element ichida qidiradi (butun ekranda emas): agar bir nechta o'xshash element bo'lsa (masalan har biri "O'chirish" tugmasiga ega ro'yxat qatorlari), avval kerakli qatorni topib (getByRole("row", { name: /Ali/ })), keyin uning ichida aniq tugmani topish mumkin (within(row).getByRole("button", ...)) — bu "bir nechta element topildi" xatosining (Xato 6) toza yechimi.rerender—renderqaytaradigan funksiya; komponentni boshqa props bilan qayta render qiladi (rerender(<Badge count={2} />)) — props o'zgarishiga komponent qanday javob berishini sinash uchun.cleanup— test orasida DOM'ni tozalaydi (RTLglobals: truebilanafterEachda avtomatik chaqiriladi — 2.2 — shuning uchun odatda qo'lda yozilmaydi). A11y test (jest-axe) — komponentning accessibility (a11y — 1.9) buzilishlarini avtomatik tekshiradi:expect(await axe(container)).toHaveNoViolations()— rang kontrasti, yetishmayotgan label, noto'g'ri ARIA kabi muammolarni tutadi. TDD (Test-Driven Development) — avval test, keyin kod yozish sikli: Red (test yoz — kod yo'q, yiqiladi) Green (minimal kod yoz — test o'tsin) Refactor (kodni tozala — test yashil qoladi). Ikki nuqta: (1)within— chegaralangan, aniq qidiruv (o'xshash elementlar orasidan to'g'risini tanlash); (2)jest-axea11y'ni avtomatik tekshiradi vagetByRole2.4-bob bilan birga yaxshi, foydalanuvchiga qulay UI'ni rag'batlantiradi (yaxshi test = yaxshi a11y).
2.14. Playwright — locator, auto-wait, fixture va CI
PLAYWRIGHT ILG'OR — locator, avtomatik kutish, fixture:
LOCATOR — element "ko'rsatkichi" (kech baholanadi — auto-wait bilan):
page.getByRole("button", { name: "Kirish" }) — RTL kabi (role/label/text afzal)
page.getByLabel("Email") / page.getByText(...) / page.locator("css")
AUTO-WAIT — Playwright element TAYYOR bo'lishini AVTOMATIK kutadi (sleep KERAK EMAS):
await page.getByRole("button").click(); // ko'rinadigan/bosiladigan bo'lguncha kutadi
await expect(page.getByText("OK")).toBeVisible(); // web-first assertion (kutadi)
AUTH SETUP — bir marta login qilib, holatni saqlab qayta ishlatish (tezlik):
// setup: login context.storageState({ path: "auth.json" })
// test: use: { storageState: "auth.json" } — har test login qilmaydi
FIXTURE — test uchun tayyor kontekst (page, browser — o'rnatilgan; maxsus ham yoziladi)
DIAGNOSTIKA: trace (qadam-baqadam yozuv), screenshot, video (xato bo'lganda)
PARALLEL — testlar parallel ishlaydi (tez); CI'da headless (10.5 — GitHub Actions)
Playwright locator'lari RTL kabi (getByRole/Label afzal — bir xil falsafa)
Auto-wait — flaky'ni kamaytiradi (sleep emas — element tayyor bo'lguncha kutadi — Xato 7)Playwright — locator, auto-wait, fixture va CI — e2e testni ishonchli va tez qiluvchi ilg'or imkoniyatlar. Locator — element "ko'rsatkichi" (darrov emas, ishlatilganda kech baholanadi): Playwright RTL bilan bir xil falsafada
getByRole,getByLabel,getByTextni afzal ko'radi (CSS/XPath — oxirgi chora), shuning uchun RTL'dan Playwright'ga o'tish tabiiy. Auto-wait — Playwright har amaldan (click,fill) oldin element tayyor (ko'rinadigan, bosiladigan) bo'lishini avtomatik kutadi, vaawait expect(locator).toBeVisible()kabi "web-first" assertion'lar shart bajarilguncha kutadi — shuning uchun qo'ldasleepyozish kerak emas (bu flaky testlarning asosiy sababini — 2.9, Xato 7 — yo'qotadi). Auth setup — kritik optimizatsiya: har e2e test'da qaytadan login qilish sekin; buning o'rniga bir marta login qilib brauzer holatini (storageState) faylga saqlab, keyingi testlarda qayta ishlatish mumkin (use: { storageState: "auth.json" }). Fixture — testga tayyor kontekst beradigan mexanizm (page,browser— o'rnatilgan; loyihaga xos fixture'lar ham yoziladi — masalan "loginlangan sahifa"). Diagnostika — xato bo'lganda Playwright trace (qadam-baqadam takroriy yozuv), screenshot va video saqlaydi (CI'da nima buzilganini ko'rish oson). Parallel va CI — testlar parallel (tez) ishlaydi, CI'da headless (ekransiz) rejimda (10.5 — GitHub Actions). Ikki nuqta: (1) Playwright locator'lari RTL kabi (getByRole/getByLabelafzal — bir xil "foydalanuvchi kabi" falsafa — 2.3); (2) auto-wait flaky testni keskin kamaytiradi (sleepemas — element tayyor bo'lguncha kutadi). Bu imkoniyatlar Playwright'ni CI'da barqaror ishlaydigan professional e2e vositasiga aylantiradi.
3. Sintaksis — tez ma'lumotnoma
TEST 2.2-bob: describe("guruh", () => { it("holat", () => { expect(x).toBe(y) }) })
RENDER 2.4-bob: render(<Component/>) + screen.getByRole("button", {name:"..."})
QUERY 2.4-bob: getBy (bor) | queryBy (yo'q) | findBy (async) | *ByRole/Text/LabelText
USER 2.5-bob: const user=userEvent.setup(); await user.click(el) / user.type(el,"x")
ASYNC 2.6-bob: await screen.findByText("...") | await waitFor(() => expect(...))
MOCK 2.7-bob: vi.fn() | vi.mock("./api") | MSW (http.get + HttpResponse.json)
HOOK 2.8-bob: const {result}=renderHook(()=>useX()); act(()=>result.current.fn())
LIFECYCLE 2.11-bob: beforeEach/afterEach/beforeAll | vi.spyOn(obj,"m") | vi.clearAllMocks()
TIMER 2.11-bob: vi.useFakeTimers(); vi.advanceTimersByTime(500); vi.useRealTimers()
ROUTER 2.12-bob: <MemoryRouter initialEntries={["/x/5"]}><Routes>...</Routes></MemoryRouter>
WITHIN 2.13-bob: within(row).getByRole("button") | rerender(<C prop={2}/>) | toMatchSnapshot()
A11Y 2.13-bob: expect(await axe(container)).toHaveNoViolations()
EXPECT: toBe | toEqual | toBeInTheDocument | toHaveBeenCalledWith | toHaveTextContent
PLAYWRIGHT 2.9-bob: test("...", async({page})=>{ await page.goto("/"); await expect(page)... })
PW-AUTH 2.14-bob: context.storageState({path:"auth.json"}) + use:{storageState:"auth.json"}4. Batafsil kod namunalari
Misol 1 — Vitest: oddiy funksiya test (2.2)
import { describe, it, expect } from "vitest";
import { calculateTotal } from "./cart";
describe("calculateTotal", () => {
it("mahsulotlar summasini hisoblaydi", () => {
const items = [{ price: 100, qty: 2 }, { price: 50, qty: 1 }];
expect(calculateTotal(items)).toBe(250); // 100*2 + 50*1
});
it("bo'sh savatda 0 qaytaradi", () => {
expect(calculateTotal([])).toBe(0);
});
});Misol 2 — Komponent render test (2.4)
import { render, screen } from "@testing-library/react";
import { describe, it, expect } from "vitest";
import { UserCard } from "./UserCard";
describe("UserCard", () => {
it("foydalanuvchi ma'lumotini ko'rsatadi", () => {
render(<UserCard name="Ali" role="admin" />);
expect(screen.getByText("Ali")).toBeInTheDocument(); // matn ko'rinadi
expect(screen.getByText("admin")).toBeInTheDocument();
expect(screen.getByRole("heading", { name: "Ali" })).toBeInTheDocument(); // role bilan
});
});Misol 3 — userEvent: hisoblagich (2.5)
import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
it("tugma bosilganda hisob oshadi", async () => {
const user = userEvent.setup();
render(<Counter />);
expect(screen.getByText("Hisob: 0")).toBeInTheDocument(); // boshlang'ich
await user.click(screen.getByRole("button", { name: "+" })); // bos
expect(screen.getByText("Hisob: 1")).toBeInTheDocument(); // oshdi
await user.click(screen.getByRole("button", { name: "+" }));
expect(screen.getByText("Hisob: 2")).toBeInTheDocument();
});Misol 4 — Forma test (userEvent — 2.5)
it("forma to'ldirilganda yuboriladi", async () => {
const user = userEvent.setup();
const onSubmit = vi.fn(); // soxta callback (2.7)
render(<LoginForm onSubmit={onSubmit} />);
await user.type(screen.getByLabelText("Email"), "ali@x.uz"); // yoz
await user.type(screen.getByLabelText("Parol"), "12345678");
await user.click(screen.getByRole("button", { name: "Kirish" }));
expect(onSubmit).toHaveBeenCalledWith({ email: "ali@x.uz", password: "12345678" }); // to'g'ri ma'lumot
});Misol 5 — Validatsiya xatosini test (2.4, 2.6)
it("noto'g'ri email xatosi ko'rsatiladi", async () => {
const user = userEvent.setup();
render(<LoginForm />);
await user.type(screen.getByLabelText("Email"), "notemail"); // noto'g'ri
await user.click(screen.getByRole("button", { name: "Kirish" }));
// findBy — xato async paydo bo'ladi (validatsiya — 11.10)
expect(await screen.findByText("Email noto'g'ri")).toBeInTheDocument();
});Misol 6 — Async ma'lumot test (findBy — 2.6)
it("ma'lumot yuklangach ro'yxatni ko'rsatadi", async () => {
render(<UserList />); // fetch boshlanadi
// 1. avval loading
expect(screen.getByText("Yuklanmoqda...")).toBeInTheDocument();
// 2. findBy — ma'lumot kelguncha kutadi
expect(await screen.findByText("Ali")).toBeInTheDocument();
expect(screen.getByText("Vali")).toBeInTheDocument();
// 3. loading yo'qoldi
expect(screen.queryByText("Yuklanmoqda...")).not.toBeInTheDocument(); // queryBy (yo'qligini)
});Misol 7 — Mock funksiya (vi.fn — 2.7)
it("o'chirish tugmasi onDelete'ni chaqiradi", async () => {
const user = userEvent.setup();
const onDelete = vi.fn(); // soxta funksiya
render(<TodoItem todo={{ id: "1", text: "Sport" }} onDelete={onDelete} />);
await user.click(screen.getByRole("button", { name: /o'chir/i }));
expect(onDelete).toHaveBeenCalledTimes(1); // bir marta chaqirildi
expect(onDelete).toHaveBeenCalledWith("1"); // to'g'ri id bilan
});Misol 8 — API mock (MSW — 2.7)
import { http, HttpResponse } from "msw";
import { setupServer } from "msw/node";
const server = setupServer(
http.get("/api/users", () => HttpResponse.json([{ id: "1", name: "Ali" }])) // soxta javob
);
beforeAll(() => server.listen());
afterEach(() => server.resetHandlers());
afterAll(() => server.close());
it("API'dan kelgan foydalanuvchilarni ko'rsatadi", async () => {
render(<UsersPage />); // haqiqiy fetch (MSW ushlaydi)
expect(await screen.findByText("Ali")).toBeInTheDocument(); // soxta javobdan
});
// Komponent HAQIQIY fetch qiladi, MSW soxta javob beradi (real kabi — 2.7)Misol 9 — Custom hook test (renderHook — 2.8)
import { renderHook, act } from "@testing-library/react";
it("useCounter oshadi va kamayadi", () => {
const { result } = renderHook(() => useCounter(5));
expect(result.current.count).toBe(5); // boshlang'ich
act(() => result.current.increment()); // state o'zgartirish — act
expect(result.current.count).toBe(6);
act(() => result.current.decrement());
expect(result.current.count).toBe(5);
});Misol 10 — Provider bilan test (Context — 2.4)
// Test uchun yordamchi — providerlar bilan render:
function renderWithProviders(ui: React.ReactElement) {
return render(
<QueryClientProvider client={new QueryClient()}>
<AuthProvider>
<BrowserRouter>{ui}</BrowserRouter>
</AuthProvider>
</QueryClientProvider>
);
}
it("login bo'lgan foydalanuvchi profilni ko'radi", async () => {
renderWithProviders(<Profile />);
expect(await screen.findByText(/profil/i)).toBeInTheDocument();
});
// Provider talab qiladigan komponent — yordamchi bilan o'ra (Context/Router/Query — 11.15)Misol 11 — Shartli holatlar test (empty/error — 2.10)
it("bo'sh ro'yxatda empty holatni ko'rsatadi", async () => {
server.use(http.get("/api/users", () => HttpResponse.json([]))); // bo'sh javob
render(<UsersPage />);
expect(await screen.findByText("Foydalanuvchilar yo'q")).toBeInTheDocument();
});
it("xato bo'lganda error holatni ko'rsatadi", async () => {
server.use(http.get("/api/users", () => new HttpResponse(null, { status: 500 }))); // xato
render(<UsersPage />);
expect(await screen.findByText(/xato/i)).toBeInTheDocument();
});
// Har holat (empty/error/data) alohida test (11.15: 2.8 — to'liq qamrov)Misol 12 — Playwright e2e: login oqimi (2.9)
import { test, expect } from "@playwright/test";
test("foydalanuvchi login qilib dashboard'ga o'tadi", async ({ page }) => {
await page.goto("/login");
await page.getByLabel("Email").fill("admin@x.uz");
await page.getByLabel("Parol").fill("12345678");
await page.getByRole("button", { name: "Kirish" }).click();
await expect(page).toHaveURL("/dashboard"); // o'tdi
await expect(page.getByRole("heading", { name: /dashboard/i })).toBeVisible();
});
test("noto'g'ri parol bilan xato ko'rsatiladi", async ({ page }) => {
await page.goto("/login");
await page.getByLabel("Email").fill("admin@x.uz");
await page.getByLabel("Parol").fill("wrong");
await page.getByRole("button", { name: "Kirish" }).click();
await expect(page.getByText(/noto'g'ri/i)).toBeVisible();
});Misol 13 — Playwright e2e: CRUD oqimi (2.9)
test("mahsulot qo'shadi va ro'yxatda ko'radi", async ({ page }) => {
await page.goto("/products");
await page.getByRole("link", { name: "+ Yangi" }).click();
await page.getByLabel("Nom").fill("Yangi mahsulot");
await page.getByLabel("Narx").fill("50000");
await page.getByRole("button", { name: "Qo'shish" }).click();
await expect(page).toHaveURL("/products");
await expect(page.getByText("Yangi mahsulot")).toBeVisible(); // ro'yxatda paydo bo'ldi
});
// Butun CRUD oqimi real brauzerda (yaratish ro'yxatda ko'rish — 2.9)Misol 14 — Test strategiyasi misoli (2.10)
// TEST QIL — murakkab mantiq (validatsiya):
it("parol kuchini to'g'ri baholaydi", () => {
expect(checkPasswordStrength("12345678")).toBe("zaif");
expect(checkPasswordStrength("Abc123!@#")).toBe("kuchli");
});
// TEST QIL — foydalanuvchi oqimi (forma):
it("forma to'g'ri ma'lumot bilan yuboriladi", async () => { /* userEvent ... */ });
// TEST QIL — regression (tuzatilgan bug):
it("0 narxli mahsulotni qabul qiladi (bug #42)", () => {
expect(calculateTotal([{ price: 0, qty: 5 }])).toBe(0); // ilgari xato berardi
});
// TEST QILMA — oddiy ko'rsatish (faqat props chizadi):
// function Logo() { return <img src="/logo.png" />; } — sinashga arzimadi (2.10)Misol 15 — Router test (MemoryRouter — 2.12)
import { render, screen } from "@testing-library/react";
import { MemoryRouter, Routes, Route } from "react-router-dom";
import { UserPage } from "./UserPage";
it("URL parametriga qarab to'g'ri foydalanuvchini ko'rsatadi", () => {
render(
<MemoryRouter initialEntries={["/users/5"]}> {/* boshlang'ich yo'l */}
<Routes>
<Route path="/users/:id" element={<UserPage />} />
</Routes>
</MemoryRouter>
);
expect(screen.getByRole("heading", { name: /foydalanuvchi 5/i })).toBeInTheDocument();
});
// MemoryRouter — brauzersiz routing test; useParams/Link/useNavigate sinaladi (11.9)Misol 16 — Fake timer: debounce hook (2.11)
import { renderHook, act } from "@testing-library/react";
import { vi } from "vitest";
import { useDebounce } from "./useDebounce";
it("qiymatni kechiktirib yangilaydi (debounce)", () => {
vi.useFakeTimers(); // vaqtni boshqaramiz
const { result, rerender } = renderHook(({ v }) => useDebounce(v, 500), {
initialProps: { v: "a" },
});
rerender({ v: "ab" }); // qiymat o'zgardi
expect(result.current).toBe("a"); // hali eski (500ms o'tmadi)
act(() => vi.advanceTimersByTime(500)); // 500ms "o'tkazamiz"
expect(result.current).toBe("ab"); // endi yangilandi
vi.useRealTimers(); // real vaqtni qaytaramiz
});
// Fake timer — debounce/setTimeout'ni real kutmasdan, tez va deterministik sinaydi (2.11)Misol 17 — Accessibility test (jest-axe — 2.13)
import { render } from "@testing-library/react";
import { axe } from "jest-axe";
import { LoginForm } from "./LoginForm";
it("a11y buzilishlari yo'q", async () => {
const { container } = render(<LoginForm />);
const results = await axe(container);
expect(results).toHaveNoViolations(); // label, kontrast, ARIA — avtomatik
});
// jest-axe — a11y'ni avtomatik tekshiradi 1.9-bob; getByRole test = yaxshi a11y (2.4)Misol 18 — Playwright: auth setup va within (2.14)
// auth.setup.ts — bir marta login qilib holatni saqlaydi (keyingi testlar qayta ishlatadi):
import { test as setup } from "@playwright/test";
setup("login qilib holatni saqlaydi", async ({ page }) => {
await page.goto("/login");
await page.getByLabel("Email").fill("admin@x.uz");
await page.getByLabel("Parol").fill("12345678");
await page.getByRole("button", { name: "Kirish" }).click();
await page.waitForURL("/dashboard");
await page.context().storageState({ path: "playwright/.auth/user.json" }); // holat saqlandi
});
// keyingi test loginlangan holatda ishlaydi (auth.json orqali — playwright.config'da use):
test("ro'yxatdan bitta qatorni o'chiradi", async ({ page }) => {
await page.goto("/users");
const row = page.getByRole("row", { name: /Ali/ }); // kerakli qator
await row.getByRole("button", { name: "O'chirish" }).click(); // shu QATOR ichida (within kabi)
await expect(page.getByText("Ali")).toBeHidden();
});
// storageState — har test qayta login qilmaydi (tez); qator ichida aniq amal (2.13, 2.14)5. To'g'ri va noto'g'ri holatlar
1) Test falsafasi
component.state.count tekshirish (implementatsiya — mo'rt — 2.3)
screen.getByText("Hisob: 5") (foydalanuvchi ko'radigan — RTL)2) Query tanlovi
getByTestId hamma joyda (a11y'ni e'tiborsiz)
getByRole/getByLabelText (foydalanuvchi/a11y kabi — 2.4)3) Async ma'lumot
getByText (fetch'dan keyin — element hali yo'q — xato — 2.6)
await findByText (paydo bo'lishini kutadi)4) Interaksiya
fireEvent.click (past daraja)
await userEvent.click (haqiqiy foydalanuvchi kabi — 2.5)5) Coverage
100% coverage maqsad (foydasiz test — vaqt isrofi — 2.10)
muhim mantiq/oqim qamralsin (sifat > raqam)6) E2e ko'lami
hamma narsani e2e (sekin, mo'rt — 2.1)
kritik yo'l e2e (login/to'lov); qolgani RTL integration6. Keng tarqalgan xatolar va yechimlari
Xato 1 — Unable to find an element with text... (async)
Sababi: getByText ishlatildi, lekin element keyinroq (fetch'dan) paydo bo'ladi 2.6-bob. Yechimi: await findByText (kutadi).
Xato 2 — not wrapped in act(...) ogohlantirishi
Sababi: state o'zgardi, lekin act/await yo'q 2.8-bob. Yechimi: userEventni await; hook state'ni act() ichida (Misol 9).
Xato 3 — Test komponent provider'siz ishlamaydi
Sababi: komponent Context/Router/Query talab qiladi 11.15-bob. Yechimi: renderWithProviders yordamchi (Misol 10).
Xato 4 — Mock API ishlamaydi (haqiqiy server'ga boradi)
Sababi: MSW server sozlanmagan yoki handler yo'q 2.7-bob. Yechimi: setupServer + beforeAll(server.listen()) (Misol 8).
Xato 5 — Test boshqa testga ta'sir qiladi (flaky)
Sababi: holat testlar orasida tozalanmagan. Yechimi: afterEach(cleanup) (RTL avtomatik), server.resetHandlers(), mock'larni tozala (vi.clearAllMocks()).
Xato 6 — getByText bir nechta element topadi (xato)
Sababi: matn bir nechta joyda. Yechimi: getAllByText (massiv), yoki aniqroq query (getByRole + name — 2.4).
Xato 7 — Playwright test mahalliy ishlaydi, CI'da yiqiladi
Sababi: vaqt (timing), tayyor bo'lmagan element. Yechimi: await expect(...).toBeVisible() (Playwright avtomatik kutadi — getByText darrov emas).
Xato 8 — Fake timer bilan test qotib qoladi yoki act ogohlantiradi
Sababi: vi.useFakeTimers() yoqilgan, lekin timer surilmagan (advanceTimersByTime yo'q) yoki useRealTimers() bilan qaytarilmagan 2.11-bob. Yechimi: timerni act(() => vi.advanceTimersByTime(ms)) bilan sur va test oxirida vi.useRealTimers() (Misol 16).
7. Integratsiya — bu mavzu stack'ning qayerida uchraydi
- Komponent/hooks (11.2-11.7): RTL komponentni, renderHook hookni sinaydi.
- Forma 11.10-bob: forma test (userEvent + validatsiya — Misol 4, 5).
- Routing 11.9-bob: Playwright navigatsiya (e2e); RTL — BrowserRouter bilan.
- Data fetching 12.4-bob: MSW bilan API mock (loading/error/empty — Misol 8, 11).
- Custom hooks 11.7-bob: renderHook (mantiq sinash — 2.8).
- a11y 1.9-bob: getByRole — a11y'ni rag'batlantiradi (yaxshi test = yaxshi a11y).
- CI/CD 10.5-bob: testlar har push'da (GitHub Actions — npm test).
- Backend test 8.11-bob: Jest unit/e2e — frontend RTL/Playwright bilan to'liq qamrov.
8. Eng yaxshi amaliyotlar (best practices)
- Foydalanuvchi kabi sina (RTL — implementatsiya emas, xulq — 2.3).
getByRole/getByLabelText(a11y kabi; testId — oxirgi chora — 2.4).userEvent(fireEvent emas) (haqiqiy interaksiya — await — 2.5).findByasync ma'lumotga (getBy darrov — 2.6).- MSW API mock'ga (tarmoq darajasida — haqiqiyroq — 2.7).
renderHookcustom hook'ga (mantiq izolyatsiya — 2.8).- E2e faqat kritik yo'lga (login/to'lov — kam, muhim — 2.9).
- Muhim joyni test qil (mantiq/oqim/bug; 100% coverage emas — 2.10).
- Bug avval test (regression — qayta kelmasin — 2.10).
- Testni izolyatsiya qil (
beforeEach+vi.clearAllMocks()— flaky yo'q — 2.11). - Fake timer taymer/debounce'ga (real vaqt kutmasdan — deterministik — 2.11).
withino'xshash elementlar orasida ("bir nechta topildi" xatosini yechadi — 2.13).- A11y'ni test qil (
jest-axe— avtomatik tekshiruv — 2.13, 1.9). - Snapshot'ni ehtiyot bilan (mo'rt — xulq testi afzal — 2.12).
- Playwright auto-wait'ga tayan (
sleepyozma — flaky kamayadi — 2.14, Xato 7). - CI/CD'da testlar (har push — avtomatik — 10.5).
9. Amaliy loyiha: "Dashboard'ni To'liq Test Qilish"
11.15-bobdagi dashboard'ni (yoki istalgan loyihani) to'liq test bilan qoplash — ishonchli, professional kod.
Maqsad
Mavjud loyihaga (dashboard) Vitest + RTL + Playwright bilan test qatlami qo'sh — unit, integration, e2e.
Talablar (requirements)
- Setup: Vitest + RTL + jsdom + userEvent 2.2-bob.
- Unit: murakkab mantiq/util (calculateTotal, validatsiya — Misol 1, 14).
- Komponent: kamida 3 komponent render + xulq test (getByRole — Misol 2, 3).
- Forma: login/CRUD forma — to'ldirish + yuborish + validatsiya (Misol 4, 5).
- Async: ma'lumot yuklanishi (findBy — Misol 6); empty/error holatlar (Misol 11).
- Mock: vi.fn (callback) + MSW (API — Misol 7, 8).
- Custom hook: kamida bitta renderHook (Misol 9).
- Provider: renderWithProviders yordamchi (Context/Router/Query — Misol 10).
- E2e (Playwright): kritik oqim — login + CRUD (Misol 12, 13).
- Strategiya: har test NEGA yozilganini izohla (muhim joy — 2.10).
Maslahatlar (hint)
- RTL — foydalanuvchi kabi (getByRole, userEvent — implementatsiya emas — 2.3).
- Async — getBy emas, findBy (Xato 1).
- API — MSW bilan mock (haqiqiy fetch + soxta javob — Misol 8).
- Provider talab qiladigan komponent — renderWithProviders (Misol 10, Xato 3).
- E2e faqat kritik yo'l (login, CRUD — hamma narsa emas — 2.1).
- Bug topsangiz — avval test yoz (regression — 2.10).
"Tayyor" mezonlari (acceptance criteria)
- Vitest + RTL sozlangan, testlar ishlaydi.
- Unit test (mantiq/util).
- Komponent test (getByRole, xulq — implementatsiya emas).
- Forma test (to'ldirish + yuborish + validatsiya).
- Async test (findBy + empty/error holatlar).
- Mock (vi.fn + MSW).
- Custom hook test (renderHook).
- Provider yordamchi (Context/Router).
- Playwright e2e (login + CRUD kritik oqim).
- Har test oqlangan (muhim joy — coverage raqami emas).
Yechim kodi ataylab berilmagan — bu loyihani o'zingiz yozib ko'ring.
10. Xulosa va keyingi bobga ko'prik
Bu bobda kodga ishonch beruvchi qatlam — testni chuqur o'rgandik:
- Nega test (ishonch, regression — 2.1); Vitest (anatomiya, AAA — 2.2); RTL falsafasi (foydalanuvchi kabi — 2.3).
- render/screen/queries (getByRole — 2.4); userEvent 2.5-bob; async (findBy/waitFor — 2.6); mocking (vi.fn/MSW — 2.7); renderHook 2.8-bob.
- Playwright e2e (real brauzer — 2.9); strategiya (nima test qilish — 2.10).
Endi siz ishonchli, professional kod yoza olasiz — refactoring va yangi feature qo'rqmasdan qilinadi (testlar buzilishni tutadi), bug regression yo'q, va kod "qanday ishlashi kerak"ligi test bilan hujjatlangan. Bu — junior va senior kodini ajratadigan muhim qatlam.
11-QISM (Frontend: React) TO'LIQ TUGADI! (11.1–11.17: DOM'dan React'ga, komponent, state, hooklar, custom hooks, code splitting, routing, forma, performance, ilg'or naqshlar, TypeScript, dashboard loyiha, animatsiya, test). Endi siz zamonaviy, professional React dasturchisisiz — interaktiv, tez, ishonchli, type-safe, jonli, sinalgan ilovalar qura olasiz.
Keyingi QISM — 12-QISM: State Management va Data Fetching. React'ni yana yuqori darajaga olib chiqamiz: Context API (chuqur), Redux Toolkit (global state — katta ilova), RTK Query va TanStack Query (server ma'lumotini professional boshqarish — kesh, sinxronlash, optimistic update), Zustand (yengil global state), va UI kutubxonalar (shadcn/ui, MUI). Bu — katta, real ilovalar uchun zarur — 11.15 dashboard'dagi ma'lumot va holat boshqaruvini professional darajaga ko'taradi.
Foydalanilgan rasmiy/ishonchli manbalar
- Vitest rasmiy hujjati (vitest.dev) —
describe/it/expect, matchers,vi.fn/vi.mock/vi.spyOn, fake timers, coverage konfiguratsiyasi - React Testing Library rasmiy hujjati (testing-library.com/react) —
render/screen, queries prioriteti (getByRole...),userEvent, async (findBy/waitFor),within/rerender/cleanup,renderHook - Kent C. Dodds — "Testing Trophy", "Common mistakes with React Testing Library", "Write tests. Not too many. Mostly integration."
- Playwright rasmiy hujjati (playwright.dev) — e2e, locator'lar, web-first assertion'lar (auto-wait), fixture,
storageStatebilan autentifikatsiya, trace/screenshot/video, parallel va CI - MSW rasmiy hujjati (mswjs.io) — tarmoq darajasida API mocking (
http,HttpResponse,setupServer) - jest-dom (
@testing-library/jest-dom) — DOM matcher'lari (toBeInTheDocument...); jest-axe — avtomatik accessibility tekshiruvi - React rasmiy hujjati (react.dev) — testing bo'yicha tavsiyalar; WAI-ARIA roles (getByRole — a11y asosi); jsdom — brauzer muhiti taqlidi
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!