IlmHamroh
Dasturlash kitobi (eski nashr)/11-QISM — React17/17-dars37 daqiqa
Mundarija (47)

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

text
  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 mantiq

Nega 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

text
  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, va vite.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

text
  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) — ichki stateni tekshiradi, mo'rt (refactoring'da buziladi — state nomini 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

text
  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'lsa null qaytaradi — "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, va getByTestId (data-testid — oxirgi chora, faqat boshqa usul yo'q bo'lsa). Ikki nuqta: (1) getByRole afzal (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

text
  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), keyin await user.click(btn), await user.type(input, "text") — har amal await bilan (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). fireEvent vs userEvent: fireEvent — eski, past daraja (bitta DOM event), userEvent — yangi, haqiqiyroq (fokus, hover, har harf alohida — real foydalanuvchi kabi). Ikki nuqta: (1) userEvent afzal (fireEvent'dan) — u haqiqiy foydalanuvchi xatti-harakatini taqlid qiladi (har harf type, fokus, hover — real test); (2) userEvent.setup() bilan boshla va har amalni await qil (haqiqiy vaqt kabi — async). Bu — interaktiv komponentlarni (forma, tugma, hisoblagich) foydalanuvchi nuqtai nazaridan sinashning standarti.

2.6. Async test — findBy va waitFor

text
  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); getBy faqat 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

text
  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 haqiqiy fetch qiladi, 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.fn callback'lar uchun, MSW API'lar uchun ideal. Mocking — komponentni tashqi bog'liqliksiz, tez va ishonchli sinashning kaliti.

2.8. Custom hook test — renderHook

text
  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 amalni act() 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 → act ichida 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

text
  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

text
  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

text
  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, spyOn va fake timers — testlarni ishonchli va tez qiluvchi mexanizmlar. Hayotiy sikl hook'lari: beforeEach (har testdan oldin — masalan vi.clearAllMocks() bilan barcha mock'larni tozalash, har test izolyatsiyalangan, avvalgisidan mustaqil bo'lishi uchun), afterEach (tozalash), beforeAll (barcha testlardan oldin bir marta — MSW server.listen()), afterAll (server.close()). vi.spyOn(object, "method") — obyektning mavjud metodini kuzatadi: chaqirildimi, qanday argumentlar bilan? Kerak bo'lsa mockImplementation bilan vaqtincha xatti-harakatini o'zgartiradi (masalan test paytida console.error shovqinini yashirish), mockRestore() bilan asl holiga qaytaradi. Fake timers (vi.useFakeTimers()) — vaqtni dasturiy boshqarish: masalan 500ms debounce'li qidiruvni sinash uchun setTimeout kutish shart emas — vi.advanceTimersByTime(500) bilan vaqtni bir lahzada 500ms oldinga surasiz va natijani tekshirasiz (test millisekundlarda o'tadi, real vaqt kutmaydi). Test oxirida vi.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

text
  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'pincha u (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 loyihada renderWithProviders yordamchi 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

text
  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 — render qaytaradigan 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 (RTL globals: true bilan afterEachda 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-axe a11y'ni avtomatik tekshiradi va getByRole 2.4-bob bilan birga yaxshi, foydalanuvchiga qulay UI'ni rag'batlantiradi (yaxshi test = yaxshi a11y).

2.14. Playwright — locator, auto-wait, fixture va CI

text
  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, va await expect(locator).toBeVisible() kabi "web-first" assertion'lar shart bajarilguncha kutadi — shuning uchun qo'lda sleep yozish 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/getByLabel afzal — bir xil "foydalanuvchi kabi" falsafa — 2.3); (2) auto-wait flaky testni keskin kamaytiradi (sleep emas — element tayyor bo'lguncha kutadi). Bu imkoniyatlar Playwright'ni CI'da barqaror ishlaydigan professional e2e vositasiga aylantiradi.


3. Sintaksis — tez ma'lumotnoma

text
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)

ts
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)

tsx
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)

tsx
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)

tsx
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)

tsx
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)

tsx
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)

tsx
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)

tsx
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)

tsx
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)

tsx
// 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)

tsx
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)

ts
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)

ts
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)

tsx
// ✅ 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)

tsx
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)

tsx
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)

tsx
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)

ts
// 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

text
❌ component.state.count tekshirish (implementatsiya — mo'rt — 2.3)
✅ screen.getByText("Hisob: 5") (foydalanuvchi ko'radigan — RTL)

2) Query tanlovi

text
❌ getByTestId hamma joyda (a11y'ni e'tiborsiz)
✅ getByRole/getByLabelText (foydalanuvchi/a11y kabi — 2.4)

3) Async ma'lumot

text
❌ getByText (fetch'dan keyin — element hali yo'q — xato — 2.6)
✅ await findByText (paydo bo'lishini kutadi)

4) Interaksiya

text
❌ fireEvent.click (past daraja)
✅ await userEvent.click (haqiqiy foydalanuvchi kabi — 2.5)

5) Coverage

text
❌ 100% coverage maqsad (foydasiz test — vaqt isrofi — 2.10)
✅ muhim mantiq/oqim qamralsin (sifat > raqam)

6) E2e ko'lami

text
❌ hamma narsani e2e (sekin, mo'rt — 2.1)
✅ kritik yo'l e2e (login/to'lov); qolgani RTL integration

6. 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).
  • findBy async ma'lumotga (getBy darrov — 2.6).
  • MSW API mock'ga (tarmoq darajasida — haqiqiyroq — 2.7).
  • renderHook custom 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).
  • within o'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 (sleep yozmaslik — 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)

  1. Setup: Vitest + RTL + jsdom + userEvent 2.2-bob.
  2. Unit: murakkab mantiq/util (calculateTotal, validatsiya — Misol 1, 14).
  3. Komponent: kamida 3 komponent render + xulq test (getByRole — Misol 2, 3).
  4. Forma: login/CRUD forma — to'ldirish + yuborish + validatsiya (Misol 4, 5).
  5. Async: ma'lumot yuklanishi (findBy — Misol 6); empty/error holatlar (Misol 11).
  6. Mock: vi.fn (callback) + MSW (API — Misol 7, 8).
  7. Custom hook: kamida bitta renderHook (Misol 9).
  8. Provider: renderWithProviders yordamchi (Context/Router/Query — Misol 10).
  9. E2e (Playwright): kritik oqim — login + CRUD (Misol 12, 13).
  10. 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, storageState bilan 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
Ulashish:Telegram'da

O'qiganingizni mustahkamlang

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
11.17-bob: Testing — Vitest, React Testing Library, Playwright — IlmHamroh