React Testing Library — Server Component testlash
Muammodan boshlaylik
Savat komponentida "−" tugmasi soni 1 bo'lganda bosilsa, mahsulot o'chishi kerak. Kimdir CSS klass nomini o'zgartirdi — test buzildi, garchi funksiya ishlasa ham. Boshqa safar tugma ishlamay qoldi — lekin test o'tdi, chunki u ichki state ni tekshirgan edi.
Testlar amalga oshirish tafsilotlarini emas, foydalanuvchi ko'radigan va qiladigan narsani tekshirishi kerak.
Hayotiy o'xshatma
Mashinani sinash. Mexanik dvigatel ichidagi har bir vintni tekshirishi mumkin — lekin xaridor uchun muhimi: kalit burilganda yonadimi, rul buriladimi, tormoz ishlaydimi. React Testing Library — xaridor nuqtai nazaridan sinov.
Asosiy tamoyil
"Testlaringiz dasturingiz qanday ishlatilishiga qanchalik o'xshasa, ular sizga shunchalik ko'p ishonch beradi."
- Elementlarni rol, matn, label bo'yicha topish — klass yoki ID bo'yicha emas
- Foydalanuvchi harakatlari —
userEventbilan (bosish, yozish) - Natijani ekranda tekshirish —
stateni emas
Client Component testi
// components/SoniTanlash.tsx
'use client'
import { useState } from 'react'
export function SoniTanlash({ boshlangich = 1, onOchir }: { boshlangich?: number; onOchir: () => void }) {
const [soni, setSoni] = useState(boshlangich)
return (
<div>
<button aria-label="Kamaytirish" onClick={() => (soni === 1 ? onOchir() : setSoni(soni - 1))}>−</button>
<span aria-live="polite">{soni}</span>
<button aria-label="Ko'paytirish" onClick={() => setSoni(soni + 1)}>+</button>
</div>
)
}
// components/SoniTanlash.test.tsx
import { describe, it, expect, vi } from 'vitest'
import { render, screen } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { SoniTanlash } from './SoniTanlash'
describe('SoniTanlash', () => {
it('+ bosilganda son oshadi', async () => {
const user = userEvent.setup()
render(<SoniTanlash onOchir={() => {}} />)
await user.click(screen.getByRole('button', { name: "Ko'paytirish" }))
expect(screen.getByText('2')).toBeInTheDocument()
})
it('1 da − bosilsa onOchir chaqiriladi', async () => {
const user = userEvent.setup()
const onOchir = vi.fn()
render(<SoniTanlash onOchir={onOchir} />)
await user.click(screen.getByRole('button', { name: 'Kamaytirish' }))
expect(onOchir).toHaveBeenCalledOnce()
})
})
Elementlarni topish
| So'rov | Ustuvorlik | Misol |
|---|---|---|
getByRole |
1 — eng yaxshi | getByRole('button', { name: 'Saqlash' }) |
getByLabelText |
Formalar uchun | getByLabelText('Email') |
getByPlaceholderText |
Label yo'q bo'lsa | — |
getByText |
Matn | getByText(/120 000 so'm/) |
getByTestId |
Oxirgi chora | data-testid="savat-jami" |
| Variant | Topilmasa | Qachon |
|---|---|---|
getBy... |
Xato | Element bo'lishi kerak |
queryBy... |
null |
Element yo'qligini tekshirish |
findBy... |
Promise, kutadi | Asinxron paydo bo'ladigan element |
expect(screen.queryByRole('alert')).not.toBeInTheDocument()
expect(await screen.findByText('Saqlandi')).toBeInTheDocument()
[!TIP]
getByRoledan foydalanish — accessibility uchun ham bepul tekshiruv: agar tugmani rol va nomi bilan topa olmasangiz, ekran o'quvchi foydalanuvchi ham uni tushunmaydi.
Forma testi
it("noto'g'ri email da xato ko'rsatadi", async () => {
const user = userEvent.setup()
render(<RoyxatForma />)
await user.type(screen.getByLabelText('Email'), 'lola')
await user.click(screen.getByRole('button', { name: "Ro'yxatdan o'tish" }))
expect(await screen.findByRole('alert')).toHaveTextContent("Email noto'g'ri")
})
Server Component lar
Sinxron Server Component — oddiy render
// components/MahsulotKarta.tsx (Server Component, async emas)
export function MahsulotKarta({ nomi, narx }: { nomi: string; narx: number }) {
return (
<article>
<h3>{nomi}</h3>
<p>{narx.toLocaleString('uz-UZ')} so'm</p>
</article>
)
}
it("nom va narxni ko'rsatadi", () => {
render(<MahsulotKarta nomi="Choynak" narx={120000} />)
expect(screen.getByRole('heading', { name: 'Choynak' })).toBeInTheDocument()
})
Async bo'lmagan Server Component — oddiy funksiya, RTL uni muammosiz render qiladi.
Async Server Component
export default async function Katalog() {
const mahsulotlar = await getMahsulotlar()
return <Royxat mahsulotlar={mahsulotlar} />
}
[!WARNING]
render(<Katalog />)— async komponentlar React Testing Library va Vitest da hozircha to'liq qo'llab-quvvatlanmaydi. Tavsiya etilgan yondashuv — ajratish:
- Ma'lumot olish (
getMahsulotlar) — unit test, bazani mock qilib- Ko'rsatish (
Royxat— sinxron, prop oladi) — RTL bilan- Ikkalasi birga, haqiqiy sahifa — Playwright (63-dars)
Kichik yo'l (ehtiyotkorlik bilan): async komponentni funksiya sifatida chaqirib, natijani render qilish:
vi.mock('@/lib/data', () => ({ getMahsulotlar: async () => [{ id: '1', nomi: 'Choynak', narx: 120000 }] }))
it('katalog', async () => {
render(await Katalog())
expect(screen.getByText('Choynak')).toBeInTheDocument()
})
Bu faqat ichida boshqa async komponentlar va server API lari (cookies()) bo'lmagan oddiy holatlarda ishlaydi.
Server Action ni test qilish
Server Action — oddiy async funksiya:
vi.mock('@/lib/dal', () => ({ talabQil: async () => ({ id: 'u1' }) }))
vi.mock('next/cache', () => ({ revalidatePath: vi.fn() }))
vi.mock('next/navigation', () => ({ redirect: vi.fn() }))
it("qisqa sarlavha — xato qaytaradi", async () => {
const fd = new FormData()
fd.set('sarlavha', 'ab')
fd.set('matn', 'yetarlicha uzun matn')
const natija = await postYarat(undefined, fd)
expect(natija?.xatolar?.sarlavha).toBeDefined()
})
Tez-tez uchraydigan xatolar
1. fireEvent o'rniga userEvent ishlatmaslik
fireEvent.click — faqat bitta hodisa; userEvent — haqiqiy foydalanuvchi kabi (focus, mousedown, mouseup, click).
2. await siz userEvent
user.click(...) — Promise; await siz keyingi tekshiruv erta ishlaydi.
3. Klass bo'yicha qidirish
container.querySelector('.btn-primary') — dizayn o'zgarsa test buziladi.
Xulosa
RTL — foydalanuvchi nuqtai nazaridan: rol, label, matn
getByRole > getByLabelText > getByText > getByTestId
getBy (bo'lishi kerak), queryBy (yo'qligi), findBy (asinxron)
userEvent.setup() + await user.click / user.type
Sinxron Server Component — oddiy render
Async Server Component — mantiqni ajrating; to'liq — Playwright
Server Action — oddiy funksiya, next/* modullarini mock qiling
Keyingi dars
63-dars: Playwright — End-to-End testing, brauzerda test
Haqiqiy brauzerda butun foydalanuvchi oqimini avtomatik sinash.