Mundarija (47)
- 1. Kirish va motivatsiya
- 2. Nazariya — chuqur tushuntirish
- 2.1. Nega test va test turlari
- 2.2. Vitest — setup va test anatomiyasi
- 2.3. React Testing Library falsafasi
- 2.4. render, screen va queries
- 2.5. userEvent — foydalanuvchi interaksiyasi
- 2.6. Async test — findBy va waitFor
- 2.7. Mocking — vi.fn, vi.mock va MSW
- 2.8. Custom hook test — renderHook
- 2.9. Playwright — e2e setup va test
- 2.10. Test strategiyasi — nima test qilish
- 2.11. Test hayotiy sikli — hook, spyOn, fake timer va coverage
- 2.12. Snapshots, visual regression va router/context test
- 2.13. Yordamchi query'lar, a11y test va TDD
- 2.14. Playwright — locator, auto-wait, fixture va CI
- 3. Sintaksis — tez ma'lumotnoma
- 4. Batafsil kod namunalari
- Misol 1 — Vitest: oddiy funksiya test (2.2)
- Misol 2 — Komponent render test (2.4)
- Misol 3 — userEvent: hisoblagich (2.5)
- Misol 4 — Forma test (userEvent — 2.5)
- Misol 5 — Validatsiya xatosini test (2.4, 2.6)
- Misol 6 — Async ma'lumot test (findBy — 2.6)
- Misol 7 — Mock funksiya (vi.fn — 2.7)
- Misol 8 — API mock (MSW — 2.7)
- Misol 9 — Custom hook test (renderHook — 2.8)
- Misol 10 — Provider bilan test (Context — 2.4)
- Misol 11 — Shartli holatlar test (empty/error — 2.10)
- Misol 12 — Playwright e2e: login oqimi (2.9)
- Misol 13 — Playwright e2e: CRUD oqimi (2.9)
- Misol 14 — Test strategiyasi misoli (2.10)
- Misol 15 — Router test (MemoryRouter — 2.12)
- Misol 16 — Fake timer: debounce hook (2.11)
- Misol 17 — Accessibility test (jest-axe — 2.13)
- Misol 18 — Playwright: auth setup va within (2.14)
- 5. To'g'ri va noto'g'ri holatlar
- 6. Keng tarqalgan xatolar va yechimlari
- 7. Integratsiya — bu mavzu stack'ning qayerida uchraydi
- 8. Eng yaxshi amaliyotlar (best practices)
- 9. Amaliy loyiha: "Dashboard'ni To'liq Test Qilish"
- Maqsad
- Talablar (requirements)
- Maslahatlar (hint)
- "Tayyor" mezonlari (acceptance criteria)
- 10. Xulosa va keyingi bobga ko'prik
- Foydalanilgan rasmiy/ishonchli manbalar
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} />→ click →expect(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 tekshir →actichida 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 (login→dashboard→amal) 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 QILMASLIK (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 yozish (qayta kelmasin), keyin tuzatish (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 yozing (regression — qayta kelmasin), keyin tuzating (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(() => { ... }) — BARCHA test'lardan OLDIN bir marta (server.listen — 2.7)
afterAll(() => { ... }) — BARCHA test'lardan KEYIN bir marta (server.close)
vi.spyOn — MAVJUD metodni "kuzatish" (soxtalashtirmasdan yoki o'rniga):
const spy = vi.spyOn(console, "error").mockImplementation(() => {});
// endi console.error chiqmaydi, lekin: expect(spy).toHaveBeenCalled();
spy.mockRestore(); // asl holiga qaytar
FAKE TIMERS — vaqtni BOSHQARISH (setTimeout/setInterval kutmaslik):
vi.useFakeTimers(); // soxta vaqt rejimiga o't
// timer bor kod chaqiriladi...
vi.advanceTimersByTime(1000); // vaqtni 1 soniya OLDINGA sur (darrov!)
vi.useRealTimers(); // test oxirida haqiqiy vaqtga qayt
❗ beforeEach'da mock reset (har test mustaqil — izolyatsiya — 2.1)
❗ fake timers — debounce/taymer testida 300ms KUTMASDAN darrov sinash (tez)Test hayotiy sikli,
spyOnva fake timers — testlarni ishonchli va tez qiluvchi mexanizmlar. Hayotiy sikl hook'lari:beforeEach(har testdan oldin — masalanvi.clearAllMocks()bilan barcha mock'larni tozalash, har test izolyatsiyalangan, avvalgisidan mustaqil bo'lishi uchun),afterEach(tozalash),beforeAll(barcha testlardan oldin bir marta — MSWserver.listen()),afterAll(server.close()).vi.spyOn(object, "method")— obyektning mavjud metodini kuzatadi: chaqirildimi, qanday argumentlar bilan? Kerak bo'lsamockImplementationbilan vaqtincha xatti-harakatini o'zgartiradi (masalan test paytidaconsole.errorshovqinini yashirish),mockRestore()bilan asl holiga qaytaradi. Fake timers (vi.useFakeTimers()) — vaqtni dasturiy boshqarish: masalan 500ms debounce'li qidiruvni sinash uchunsetTimeoutkutish shart emas —vi.advanceTimersByTime(500)bilan vaqtni bir lahzada 500ms oldinga surasiz va natijani tekshirasiz (test millisekundlarda o'tadi, real vaqt kutmaydi). Test oxiridavi.useRealTimers()bilan haqiqiy vaqtga qaytish shart (aks holda boshqa testlar buziladi). Ikki nuqta: (1) test izolyatsiyasi —beforeEachda tozalash (bir test boshqasiga ta'sir qilmasin — "flaky test"larning eng keng sababi); (2) fake timer deterministik va chaqqon test beradi (debounce, animatsiya, taymer kutmasdan).
2.12. Snapshots, visual regression va router/context test
SNAPSHOT TEST (Vitest) — komponent chiqishini "suratga" olish:
expect(container).toMatchSnapshot();
// → birinchi marta: __snapshots__/ fayl yaratadi (HTML saqlanadi)
// → keyingi marta: hozirgi chiqish fayldagi bilan mosmi? (farq bo'lsa xato)
// ❗ EHTIYOT BO'LING: snapshot oson yangilanadi (u tugmasi), mo'rt;
// xulq testi (getByRole) ANCHA ishonchli — snapshot faqat cheklangan joyga
ROUTER'LI KOMPONENT TESTI — MemoryRouter bilan o'rash:
render(
<MemoryRouter initialEntries={["/users/42"]}>
<Routes>
<Route path="/users/:id" element={<UserProfile />} />
</Routes>
</MemoryRouter>
);
// → useParams() { id: "42" } oladi — URL'ni sinash mumkin 11.9-bob
CONTEXT'LI KOMPONENT TESTI — Provider bilan o'rash (yoki renderWithProviders):
render(<AuthProvider><Dashboard /></AuthProvider>);
❗ Router/Context talab qiladigan komponentni TESTDA ham o'rash SHART (aks holda xato — Xato 3)
❗ Snapshot — yordamchi, ASOS emas (xulq testi ancha qadrliroq)Snapshots va wrapper'lar (Router/Context) — maxsus holatlarni sinash. Snapshot test (
toMatchSnapshot) — komponentning DOM chiqishini matn faylga saqlaydi; keyingi testlarda chiqish o'zgarganini solishtiradi. Diqqat: snapshot testga ortiqcha suyanmang — dasturchilar ko'pinchau(update) tugmasini o'ylab o'tirmay bosib yuboradi va xatoni o'tkazib yuboradi; RTL'ning xulq testi (getByRole, userEvent) ancha ishonchliroq. Snapshot faqat qat'iy formatlar (xato xabarlari, kichik UI qismlari) uchun foydali. Wrapper'lar — komponent tashqi kontekstni (Router, Context, React Query) talab qilsa, testda ham uni shu provayder bilan o'rab render qilish kerak:MemoryRouter(React Router) — virtual tarix (initialEntries={["/users/42"]}), brauzer URL'isiz routing'ni sinash imkonini beradi;AuthProvider/QueryClientProvider— Context'larni berish. Buni soddalashtirish uchun loyihadarenderWithProvidersyordamchi funksiyasi yoziladi (Misol 10) — har testda 5 ta provayder yozmaslik uchun. Tamoyil: komponent nimaga bog'liq bo'lsa — testda uni ta'minlang (MemoryRouter, Provider); lekin ichki kodni emas, foydalanuvchi ko'radigan natijani tekshiring.
2.13. Yordamchi query'lar, a11y test va TDD
WITHIN — element ICHIDA qidirish (chegaralangan):
const row = screen.getByRole("row", { name: /Ali/ });
const delBtn = within(row).getByRole("button", { name: /O'chir/ });
// → butun ekranda emas, FAQAT shu qator ichidan topadi
A11Y TEST (jest-axe) — avtomatik accessibility tekshiruvi:
import { axe, toHaveNoViolations } from "jest-axe";
expect.extend(toHaveNoViolations);
const { container } = render(<Form />);
expect(await axe(container)).toHaveNoViolations(); // a11y buzilishi bormi?
TDD (Test-Driven Development) — avval TEST, keyin KOD:
1. Red — test yozish (hali kod yo'q — yiqiladi)
2. Green — minimal kod yozish (test o'tsin)
3. Refactor — kodni tozalash (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 yozish — kod yo'q, yiqiladi) → Green (minimal kod yozish — test o'tsin) → Refactor (kodni tozalash — 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 QILMASLIK — 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 tozalang (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 suring 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 sinash (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 qilish (mantiq/oqim/bug; 100% coverage emas — 2.10).
- Bug → avval test (regression — qayta kelmasin — 2.10).
- Testni izolyatsiya qilish (
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 qilish (
jest-axe— avtomatik tekshiruv — 2.13, 1.9). - Snapshot'ni ehtiyot bilan (mo'rt — xulq testi afzal — 2.12).
- Playwright auto-wait'ga tayanish (
sleepyozmaslik — 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'shing — 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 izohlang (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 yozing (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
O'qiganingizni mustahkamlang
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!