Daraja 10 β Testing¶
β¬ οΈ Oldingi: Daraja 9 β TypeScript bilan React Β· π README Β· Keyingi: Daraja 11 β Production va ekotizim β‘οΈ
Sifatli kod = testlangan kod. Vitest (Vite'ga tabiiy) + React Testing Library standart.
npm install -D vitest @testing-library/react @testing-library/jest-dom @testing-library/user-event jsdom
10.1. Test falsafasi¶
RTL qoidasi: "Foydalanuvchi qanday ishlatsa, shunday test qiling." Implementatsiya detallarini emas (state, ichki funksiya), xulq-atvorni (ekranda nima ko'rinadi, nima bosiladi) tekshiring.
10.2. Birinchi test¶
// Button.jsx
export function Button({ onClick, children }) {
return <button onClick={onClick}>{children}</button>;
}
// Button.test.jsx
import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { expect, test, vi } from "vitest";
import { Button } from "./Button";
test("tugma bosilganda funksiya chaqiriladi", async () => {
const handleClick = vi.fn(); // mock funksiya
render(<Button onClick={handleClick}>Bosing</Button>);
await userEvent.click(screen.getByText("Bosing"));
expect(handleClick).toHaveBeenCalledTimes(1);
});
Har bir RTL testi bir xil to'rt bosqichli oqimni bosib o'tadi β komponentni chizish, elementni topish, u bilan ishlash va natijani tekshirish:
10.3. Element topish (queries)¶
screen.getByRole("button", { name: "Yuborish" }); // afzal β a11y
screen.getByLabelText("Email");
screen.getByPlaceholderText("Yozing...");
screen.getByText("Salom");
screen.getByTestId("user-card"); // oxirgi chora
// findBy β async (kutadi)
await screen.findByText("Yuklandi");
// queryBy β yo'qligini tekshirish uchun (null qaytaradi, xato bermaydi)
expect(screen.queryByText("Xato")).not.toBeInTheDocument();
10.4. State va interaktivlikni test qilish¶
test("counter oshadi", async () => {
render(<Counter />);
expect(screen.getByText("0")).toBeInTheDocument();
await userEvent.click(screen.getByRole("button", { name: "+1" }));
expect(screen.getByText("1")).toBeInTheDocument();
});
10.5. Async va API mock¶
test("foydalanuvchilar yuklanadi", async () => {
// fetch'ni mock qilamiz
global.fetch = vi.fn(() =>
Promise.resolve({ json: () => Promise.resolve([{ id: 1, name: "Ali" }]) })
);
render(<Users />);
expect(screen.getByText("Yuklanmoqda...")).toBeInTheDocument();
expect(await screen.findByText("Ali")).toBeInTheDocument();
});
Real loyihada
fetchni qo'lda mock qilish o'rniga MSW (Mock Service Worker) ishlatiladi β tarmoq darajasida mock, eng ishonchli yondashuv.
10.6. Custom hook'ni test qilish¶
import { renderHook, act } from "@testing-library/react";
test("useToggle ishlaydi", () => {
const { result } = renderHook(() => useToggle());
expect(result.current[0]).toBe(false);
act(() => result.current[1]()); // toggle
expect(result.current[0]).toBe(true);
});
10.7. Testlar turlari¶
- Unit β bitta funksiya/komponent (eng ko'p).
- Integration β bir nechta komponent birga (eng qimmatli).
- E2E β butun ilova brauzerda (Playwright / Cypress).
Bu uch turning nisbati piramida shaklida bo'ladi: tagida ko'p tez unit test, yuqorida esa kam, sekin E2E test:
10.8. Keng tarqalgan xatolar¶
| Xato | To'g'risi |
|---|---|
| Implementatsiya detalini test qilish (state) | Xulq-atvorni test qiling |
getByTestIdni asosiy usul qilish |
getByRole/getByText afzal |
Async'da getBy ishlatish |
findBy (kutadi) |
userEvent'ni awaitsiz ishlatish |
Har doim await |
fetchni qo'lda mock qilish |
MSW |
+20 Masala β Daraja 10¶
Oson:
1. Button bosilganda mock funksiya chaqirilishini test qiling.
2. Komponent matnni to'g'ri ko'rsatishini tekshiring.
3. getByRole bilan tugma toping.
4. Props o'tkazib, to'g'ri render bo'lishini test qiling.
5. disabled tugma bosilmasligini tekshiring.
6. Conditional rendering'ni test qiling (queryBy bilan yo'qligi).
7. Input'ga yozilganini userEvent.type bilan test qiling.
8. Snapshot test yozing.
O'rta:
9. Counter komponentini to'liq test qiling (+/-/reset).
10. Forma submit'ini test qiling (mock onSubmit).
11. Validatsiya xatosi ko'rinishini test qiling.
12. Todo qo'shish/o'chirishni test qiling.
13. findBy bilan async yuklanishni test qiling.
14. useToggle custom hook'ini renderHook bilan test qiling.
15. Mock fetch bilan API komponentini test qiling.
Qiyin:
16. MSW bilan API'ni mock qilib, integration test yozing.
17. TanStack Query komponentini test qiling (QueryClient wrapper bilan).
18. useReducer'li murakkab hook'ni to'liq test qiling.
19. Modal/portal komponentini test qiling.
20. Playwright bilan login β dashboard E2E test stsenariysi yozing.
β Qiyin masalalar yechimi (16β20)
17 β TanStack Query komponentini test qilish (QueryClient wrapper):
function renderWithClient(ui: React.ReactElement) {
const client = new QueryClient({ defaultOptions: { queries: { retry: false } } });
return render(<QueryClientProvider client={client}>{ui}</QueryClientProvider>);
}
it("o'quvchilarni ko'rsatadi", async () => {
renderWithClient(<StudentList />);
await waitFor(() => expect(screen.getByText("Ali Valiyev")).toBeInTheDocument());
});
retry: false β xato bo'lsa darrov ko'rsin, qayta urinmasin.
16 β MSW bilan API mock (integration test):
import { http, HttpResponse } from "msw";
import { setupServer } from "msw/node";
const server = setupServer(
http.get("/api/students", () =>
HttpResponse.json([{ id: 1, name: "Ali Valiyev" }])
)
);
beforeAll(() => server.listen());
afterEach(() => server.resetHandlers());
afterAll(() => server.close());
20 β Playwright E2E (login β dashboard):
import { test, expect } from "@playwright/test";
test("login -> dashboard", async ({ page }) => {
await page.goto("/login");
await page.getByLabel("Email").fill("ali@mail.uz");
await page.getByLabel("Parol").fill("12345678");
await page.getByRole("button", { name: "Kirish" }).click();
await expect(page).toHaveURL("/dashboard");
});
Tamoyil:
getByRole/getByLabelbilan qidir (foydalanuvchi ko'rganidek),getByTestIdni kamroq ishlat. Test foydalanuvchi xulqini tekshirsin, implementatsiyani emas. (18 βuseReducerhook'i:renderHook+actbilan; 19 β Modal/portal:getByRole("dialog")bilan tekshiriladi.)