Ushbu to'liq qo'llanma yordamida React Testing Library'ni (RTL) o'zlashtiring. Eng yaxshi amaliyotlar va real misollarga e'tibor qaratgan holda, React ilovalaringiz uchun samarali va foydalanuvchiga yo'naltirilgan testlarni yozishni o'rganing.
React Testing Library: To'liq Qo'llanma
Bugungi tez sur'atlarda rivojlanayotgan veb-dasturlash landshaftida React ilovalaringizning sifati va ishonchliligini ta'minlash birinchi darajali ahamiyatga ega. React Testing Library (RTL) foydalanuvchi nuqtai nazariga e'tibor qaratadigan testlarni yozish uchun mashhur va samarali yechim sifatida paydo bo'ldi. Ushbu qo'llanma RTL haqida to'liq ma'lumot beradi, fundamental tushunchalardan tortib ilg'or texnikalargacha bo'lgan hamma narsani qamrab oladi va sizga mustahkam va qo'llab-quvvatlanadigan React ilovalarini yaratish imkoniyatini beradi.
Nima uchun React Testing Library'ni tanlash kerak?
An'anaviy testlash yondashuvlari ko'pincha amalga oshirish detallariga tayanadi, bu esa testlarni mo'rt qiladi va kichik kod o'zgarishlarida sinishga moyil bo'ladi. Boshqa tomondan, RTL sizni komponentlaringizni foydalanuvchi ular bilan qanday o'zaro aloqada bo'lishini sinab ko'rishga undaydi, foydalanuvchi ko'radigan va boshdan kechiradigan narsalarga e'tibor qaratadi. Bu yondashuv bir nechta asosiy afzalliklarni taqdim etadi:
- Foydalanuvchiga yo'naltirilgan testlash: RTL foydalanuvchining nuqtai nazarini aks ettiruvchi testlar yozishni rag'batlantiradi, bu sizning ilovangiz oxirgi foydalanuvchi nuqtai nazaridan kutilganidek ishlashini ta'minlaydi.
- Testlarning mo'rtligini kamaytirish: Amalga oshirish detallarini testlashdan qochish orqali, RTL testlari kodingizni refaktoring qilganingizda sinishi ehtimoli kamroq bo'ladi, bu esa yanada qo'llab-quvvatlanadigan va mustahkam testlarga olib keladi.
- Yaxshilangan kod dizayni: RTL sizni qulay va ishlatish oson bo'lgan komponentlar yozishga undaydi, bu esa umumiy kod dizaynining yaxshilanishiga olib keladi.
- Qulaylikka e'tibor: RTL komponentlaringizning qulayligini (accessibility) testlashni osonlashtiradi, bu sizning ilovangiz hamma uchun foydalanishga yaroqli bo'lishini ta'minlaydi.
- Soddalashtirilgan testlash jarayoni: RTL oddiy va intuitiv API taqdim etadi, bu esa testlarni yozish va qo'llab-quvvatlashni osonlashtiradi.
Testlash Muhitini Sozlash
RTL dan foydalanishni boshlashdan oldin, siz testlash muhitingizni sozlashingiz kerak. Bu odatda kerakli bog'liqliklarni o'rnatish va testlash freymvorkingizni sozlashni o'z ichiga oladi.
Dastlabki talablar
- Node.js va npm (yoki yarn): Tizimingizda Node.js va npm (yoki yarn) o'rnatilganligiga ishonch hosil qiling. Ularni rasmiy Node.js veb-saytidan yuklab olishingiz mumkin.
- React Loyihasi: Sizda mavjud React loyihasi bo'lishi yoki Create React App yoki shunga o'xshash vosita yordamida yangisini yaratishingiz kerak.
O'rnatish
Quyidagi paketlarni npm yoki yarn yordamida o'rnating:
npm install --save-dev @testing-library/react @testing-library/jest-dom jest babel-jest @babel/preset-env @babel/preset-react
Yoki, yarn yordamida:
yarn add --dev @testing-library/react @testing-library/jest-dom jest babel-jest @babel/preset-env @babel/preset-react
Paketlar tavsifi:
- @testing-library/react: React komponentlarini testlash uchun asosiy kutubxona.
- @testing-library/jest-dom: DOM tugunlari haqida tasdiqlash uchun maxsus Jest moslashtirgichlarini (matchers) taqdim etadi.
- Jest: Mashhur JavaScript testlash freymvorki.
- babel-jest: Kodingizni kompilyatsiya qilish uchun Babel'dan foydalanadigan Jest transformatori.
- @babel/preset-env: Maqsadli muhitlaringizni qo'llab-quvvatlash uchun zarur bo'lgan Babel plaginlari va presetlarini aniqlaydigan Babel preseti.
- @babel/preset-react: React uchun Babel preseti.
Konfiguratsiya
Loyihangizning ildizida quyidagi tarkibga ega `babel.config.js` faylini yarating:
module.exports = {
presets: ['@babel/preset-env', '@babel/preset-react'],
};
`package.json` faylingizni test skriptini qo'shish uchun yangilang:
{
"scripts": {
"test": "jest"
}
}
Loyihangizning ildizida Jest'ni sozlash uchun `jest.config.js` faylini yarating. Minimal konfiguratsiya quyidagicha ko'rinishi mumkin:
module.exports = {
testEnvironment: 'jsdom',
setupFilesAfterEnv: ['/src/setupTests.js'],
};
Quyidagi tarkibga ega `src/setupTests.js` faylini yarating. Bu Jest DOM moslashtirgichlari barcha testlaringizda mavjud bo'lishini ta'minlaydi:
import '@testing-library/jest-dom/extend-expect';
Birinchi Testingizni Yozish
Keling, oddiy misoldan boshlaymiz. Aytaylik, sizda salomlashuv xabarini ko'rsatadigan React komponenti bor:
// src/components/Greeting.js
import React from 'react';
function Greeting({ name }) {
return <h1>Salom, {name}!</h1>;
}
export default Greeting;
Endi, ushbu komponent uchun test yozamiz:
// src/components/Greeting.test.js
import React from 'react';
import { render, screen } from '@testing-library/react';
import Greeting from './Greeting';
test('salomlashuv xabarini render qiladi', () => {
render(<Greeting name="Dunyo" />);
const greetingElement = screen.getByText(/Salom, Dunyo!/i);
expect(greetingElement).toBeInTheDocument();
});
Tushuntirish:
- `render`: Bu funksiya komponentni DOM'ga render qiladi.
- `screen`: Bu obyekt DOM'ni so'rov qilish uchun metodlar taqdim etadi.
- `getByText`: Bu metod elementni uning matn tarkibi bo'yicha topadi. `/i` belgisi qidiruvni registrga sezgir bo'lmagan holga keltiradi.
- `expect`: Bu funksiya komponentning xatti-harakatlari haqida tasdiqlar qilish uchun ishlatiladi.
- `toBeInTheDocument`: Bu moslashtirgich elementning DOM'da mavjudligini tasdiqlaydi.
Testni ishga tushirish uchun terminalingizda quyidagi buyruqni bajaring:
npm test
Agar hamma narsa to'g'ri sozlangan bo'lsa, test muvaffaqiyatli o'tishi kerak.
Keng tarqalgan RTL so'rovlari
RTL DOM'dagi elementlarni topish uchun turli xil so'rov usullarini taqdim etadi. Ushbu so'rovlar foydalanuvchilarning ilovangiz bilan qanday o'zaro ta'sir qilishini taqlid qilish uchun mo'ljallangan.
`getByRole`
Bu so'rov elementni uning ARIA roli bo'yicha topadi. Iloji boricha `getByRole` dan foydalanish yaxshi amaliyotdir, chunki bu qulaylikni (accessibility) oshiradi va testlaringizning asosiy DOM tuzilmasidagi o'zgarishlarga chidamli bo'lishini ta'minlaydi.
<button role="button">Meni bosing</button>
const buttonElement = screen.getByRole('button');
expect(buttonElement).toBeInTheDocument();
`getByLabelText`
Bu so'rov elementni unga bog'langan yorliq matni bo'yicha topadi. Bu forma elementlarini testlash uchun foydalidir.
<label htmlFor="name">Ism:</label>
<input type="text" id="name" />
const nameInputElement = screen.getByLabelText('Ism:');
expect(nameInputElement).toBeInTheDocument();
`getByPlaceholderText`
Bu so'rov elementni uning ko'rsatma matni (placeholder text) bo'yicha topadi.
<input type="text" placeholder="Emailingizni kiriting" />
const emailInputElement = screen.getByPlaceholderText('Emailingizni kiriting');
expect(emailInputElement).toBeInTheDocument();
`getByAltText`
Bu so'rov rasm elementini uning alt matni bo'yicha topadi. Qulaylikni ta'minlash uchun barcha rasmlar uchun mazmunli alt matnini taqdim etish muhimdir.
<img src="logo.png" alt="Kompaniya Logotipi" />
const logoImageElement = screen.getByAltText('Kompaniya Logotipi');
expect(logoImageElement).toBeInTheDocument();
`getByTitle`
Bu so'rov elementni uning sarlavha (title) atributi bo'yicha topadi.
<span title="Yopish">X</span>
const closeElement = screen.getByTitle('Yopish');
expect(closeElement).toBeInTheDocument();
`getByDisplayValue`
Bu so'rov elementni uning ko'rsatiladigan qiymati (display value) bo'yicha topadi. Bu oldindan to'ldirilgan qiymatlarga ega bo'lgan forma kiritishlarini testlash uchun foydalidir.
<input type="text" value="Boshlang'ich Qiymat" />
const inputElement = screen.getByDisplayValue('Boshlang\'ich Qiymat');
expect(inputElement).toBeInTheDocument();
`getAllBy*` so'rovlari
`getBy*` so'rovlariga qo'shimcha ravishda, RTL `getAllBy*` so'rovlarini ham taqdim etadi, ular mos keladigan elementlar massivini qaytaradi. Bular bir xil xususiyatlarga ega bo'lgan bir nechta elementning DOM'da mavjudligini tasdiqlash kerak bo'lganda foydalidir.
<li>Element 1</li>
<li>Element 2</li>
<li>Element 3</li>
const listItems = screen.getAllByRole('listitem');
expect(listItems).toHaveLength(3);
`queryBy*` so'rovlari
`queryBy*` so'rovlari `getBy*` so'rovlariga o'xshaydi, lekin ular mos keladigan element topilmasa, xato chiqarish o'rniga `null` qiymatini qaytaradi. Bu elementning DOM'da mavjud *emasligini* tasdiqlamoqchi bo'lganingizda foydalidir.
const missingElement = screen.queryByText('Mavjud bo\'lmagan matn');
expect(missingElement).toBeNull();
`findBy*` so'rovlari
`findBy*` so'rovlari `getBy*` so'rovlarining asinxron versiyalaridir. Ular mos keladigan element topilganda hal bo'ladigan Promise qaytaradi. Bular API'dan ma'lumotlarni olish kabi asinxron operatsiyalarni testlash uchun foydalidir.
// Asinxron ma'lumotlar yuklanishini simulyatsiya qilish
const fetchData = () => new Promise(resolve => {
setTimeout(() => resolve('Ma\'lumotlar Yuklandi!'), 1000);
});
function MyComponent() {
const [data, setData] = React.useState(null);
React.useEffect(() => {
fetchData().then(setData);
}, []);
return <div>{data}</div>;
}
test('ma\'lumotlarni asinxron yuklaydi', async () => {
render(<MyComponent />);
const dataElement = await screen.findByText('Ma\'lumotlar Yuklandi!');
expect(dataElement).toBeInTheDocument();
});
Foydalanuvchi O'zaro Ta'sirlarini Simulyatsiya Qilish
RTL tugmalarni bosish, kiritish maydonlariga yozish va formalarni yuborish kabi foydalanuvchi o'zaro ta'sirlarini simulyatsiya qilish uchun `fireEvent` va `userEvent` API'larini taqdim etadi.
`fireEvent`
`fireEvent` sizga dasturiy ravishda DOM hodisalarini ishga tushirish imkonini beradi. Bu ishga tushiriladigan hodisalar ustidan nozik nazoratni ta'minlaydigan quyi darajadagi API'dir.
<button onClick={() => alert('Tugma bosildi!')}>Meni bosing</button>
import { fireEvent } from '@testing-library/react';
test('tugma bosilishini simulyatsiya qiladi', () => {
const alertMock = jest.spyOn(window, 'alert').mockImplementation(() => {});
render(<button onClick={() => alert('Tugma bosildi!')}>Meni bosing</button>);
const buttonElement = screen.getByRole('button');
fireEvent.click(buttonElement);
expect(alertMock).toHaveBeenCalledTimes(1);
alertMock.mockRestore();
});
`userEvent`
`userEvent` foydalanuvchi o'zaro ta'sirlarini yanada realistik tarzda simulyatsiya qiladigan yuqori darajadagi API'dir. U fokusni boshqarish va hodisalar ketma-ketligi kabi tafsilotlarni boshqaradi, bu esa testlaringizni yanada mustahkam va kamroq mo'rt qiladi.
<input type="text" onChange={e => console.log(e.target.value)} />
import userEvent from '@testing-library/user-event';
test('kiritish maydoniga yozishni simulyatsiya qiladi', () => {
const inputElement = screen.getByRole('textbox');
userEvent.type(inputElement, 'Salom, dunyo!');
expect(inputElement).toHaveValue('Salom, dunyo!');
});
Asinxron Kodni Testlash
Ko'pgina React ilovalari API'dan ma'lumotlarni olish kabi asinxron operatsiyalarni o'z ichiga oladi. RTL asinxron kodni testlash uchun bir nechta vositalarni taqdim etadi.
`waitFor`
`waitFor` tasdiqlashdan oldin biror shartning rost bo'lishini kutishga imkon beradi. Bu tugashi uchun biroz vaqt talab qiladigan asinxron operatsiyalarni testlash uchun foydalidir.
function MyComponent() {
const [data, setData] = React.useState(null);
React.useEffect(() => {
setTimeout(() => {
setData('Ma\'lumot yuklandi!');
}, 1000);
}, []);
return <div>{data}</div>;
}
import { waitFor } from '@testing-library/react';
test('ma\'lumotlar yuklanishini kutadi', async () => {
render(<MyComponent />);
await waitFor(() => screen.getByText('Ma\'lumot yuklandi!'));
const dataElement = screen.getByText('Ma\'lumot yuklandi!');
expect(dataElement).toBeInTheDocument();
});
`findBy*` so'rovlari
Yuqorida aytib o'tilganidek, `findBy*` so'rovlari asinxron bo'lib, mos keladigan element topilganda hal bo'ladigan Promise qaytaradi. Bular DOM'dagi o'zgarishlarga olib keladigan asinxron operatsiyalarni testlash uchun foydalidir.
Hook'larni Testlash
React Hook'lari holatli mantiqni o'z ichiga olgan qayta ishlatiladigan funksiyalardir. RTL maxsus Hook'larni alohida testlash uchun `@testing-library/react-hooks`'dan `renderHook` yordamchi dasturini taqdim etadi (v17 dan boshlab bekor qilingan va to'g'ridan-to'g'ri `@testing-library/react` foydasiga o'zgartirilgan).
// src/hooks/useCounter.js
import { useState } from 'react';
function useCounter(initialValue = 0) {
const [count, setCount] = useState(initialValue);
const increment = () => {
setCount(prevCount => prevCount + 1);
};
const decrement = () => {
setCount(prevCount => prevCount - 1);
};
return { count, increment, decrement };
}
export default useCounter;
// src/hooks/useCounter.test.js
import { renderHook, act } from '@testing-library/react';
import useCounter from './useCounter';
test('hisoblagichni oshiradi', () => {
const { result } = renderHook(() => useCounter());
act(() => {
result.current.increment();
});
expect(result.current.count).toBe(1);
});
Tushuntirish:
- `renderHook`: Bu funksiya Hook'ni render qiladi va Hook natijasini o'z ichiga olgan obyektni qaytaradi.
- `act`: Bu funksiya holat yangilanishlariga sabab bo'ladigan har qanday kodni o'rash uchun ishlatiladi. Bu React'ning yangilanishlarni to'g'ri guruhlashi va qayta ishlashini ta'minlaydi.
Ilg'or Testlash Texnikalari
RTL asoslarini o'zlashtirganingizdan so'ng, testlaringiz sifati va qo'llab-quvvatlanishini yaxshilash uchun yanada ilg'or testlash texnikalarini o'rganishingiz mumkin.
Modullarni Mocking Qilish
Ba'zan, komponentlaringizni izolyatsiya qilish va testlash paytida ularning xatti-harakatlarini nazorat qilish uchun tashqi modullar yoki bog'liqliklarni mock qilishingiz (soxtalashtirishingiz) kerak bo'lishi mumkin. Jest bu maqsadda kuchli mocking API'sini taqdim etadi.
// src/api/dataService.js
export const fetchData = async () => {
const response = await fetch('/api/data');
const data = await response.json();
return data;
};
// src/components/MyComponent.js
import React, { useState, useEffect } from 'react';
import { fetchData } from '../api/dataService';
function MyComponent() {
const [data, setData] = useState(null);
useEffect(() => {
fetchData().then(setData);
}, []);
return <div>{data ? data.message : null}</div>;
}
// src/components/MyComponent.test.js
import { render, screen, waitFor } from '@testing-library/react';
import MyComponent from './MyComponent';
import * as dataService from '../api/dataService';
jest.mock('../api/dataService');
test('API dan ma\'lumotlarni oladi', async () => {
dataService.fetchData.mockResolvedValue({ message: 'Soxta ma\'lumotlar!' });
render(<MyComponent />);
await waitFor(() => screen.getByText('Soxta ma\'lumotlar!'));
expect(screen.getByText('Soxta ma\'lumotlar!')).toBeInTheDocument();
expect(dataService.fetchData).toHaveBeenCalledTimes(1);
});
Tushuntirish:
- `jest.mock('../api/dataService')`: Bu qator `dataService` modulini mock qiladi.
- `dataService.fetchData.mockResolvedValue({ message: 'Soxta ma\'lumotlar!' })`: Bu qator mock qilingan `fetchData` funksiyasini belgilangan ma'lumotlar bilan hal bo'ladigan Promise qaytaradigan qilib sozlaydi.
- `expect(dataService.fetchData).toHaveBeenCalledTimes(1)`: Bu qator mock qilingan `fetchData` funksiyasi bir marta chaqirilganligini tasdiqlaydi.
Kontekst Provayderlari
Agar sizning komponentingiz Kontekst Provayderiga bog'liq bo'lsa, testlash paytida komponentingizni provayder bilan o'rashingiz kerak bo'ladi. Bu komponentning kontekst qiymatlariga kirishini ta'minlaydi.
// src/contexts/ThemeContext.js
import React, { createContext, useState } from 'react';
export const ThemeContext = createContext();
export function ThemeProvider({ children }) {
const [theme, setTheme] = useState('light');
const toggleTheme = () => {
setTheme(prevTheme => (prevTheme === 'light' ? 'dark' : 'light'));
};
return (
<ThemeContext.Provider value={{ theme, toggleTheme }}>
{children}
</ThemeContext.Provider>
);
}
// src/components/MyComponent.js
import React, { useContext } from 'react';
import { ThemeContext } from '../contexts/ThemeContext';
function MyComponent() {
const { theme, toggleTheme } = useContext(ThemeContext);
return (
<div style={{ backgroundColor: theme === 'light' ? '#fff' : '#000', color: theme === 'light' ? '#000' : '#fff' }}>
<p>Joriy mavzu: {theme}</p>
<button onClick={toggleTheme}>Mavzuni o'zgartirish</button>
</div>
);
}
// src/components/MyComponent.test.js
import { render, screen, fireEvent } from '@testing-library/react';
import MyComponent from './MyComponent';
import { ThemeProvider } from '../contexts/ThemeContext';
test('mavzuni o\'zgartiradi', () => {
render(
<ThemeProvider>
<MyComponent />
</ThemeProvider>
);
const themeParagraph = screen.getByText(/Joriy mavzu: light/i);
const toggleButton = screen.getByRole('button', { name: /Mavzuni o'zgartirish/i });
expect(themeParagraph).toBeInTheDocument();
fireEvent.click(toggleButton);
expect(screen.getByText(/Joriy mavzu: dark/i)).toBeInTheDocument();
});
Tushuntirish:
- Test paytida kerakli kontekstni ta'minlash uchun `MyComponent`'ni `ThemeProvider` bilan o'raymiz.
Router bilan Testlash
React Router'dan foydalanadigan komponentlarni testlashda, sizga soxta Router kontekstini taqdim etish kerak bo'ladi. Bunga `react-router-dom`'dan `MemoryRouter` komponentidan foydalanib erishishingiz mumkin.
// src/components/MyComponent.js
import React from 'react';
import { Link } from 'react-router-dom';
function MyComponent() {
return (
<div>
<Link to="/about">Haqida sahifasiga o'tish</Link>
</div>
);
}
// src/components/MyComponent.test.js
import { render, screen } from '@testing-library/react';
import { MemoryRouter } from 'react-router-dom';
import MyComponent from './MyComponent';
test('haqida sahifasiga havolani render qiladi', () => {
render(
<MemoryRouter>
<MyComponent />
</MemoryRouter>
);
const linkElement = screen.getByRole('link', { name: /Haqida sahifasiga o'tish/i });
expect(linkElement).toBeInTheDocument();
expect(linkElement).toHaveAttribute('href', '/about');
});
Tushuntirish:
- Soxta Router kontekstini ta'minlash uchun `MyComponent`'ni `MemoryRouter` bilan o'raymiz.
- Havola elementining to'g'ri `href` atributiga ega ekanligini tasdiqlaymiz.
Samarali Testlarni Yozish uchun Eng Yaxshi Amaliyotlar
RTL bilan testlar yozishda amal qilish kerak bo'lgan ba'zi eng yaxshi amaliyotlar:
- Foydalanuvchi o'zaro ta'sirlariga e'tibor qarating: Foydalanuvchilar ilovangiz bilan qanday o'zaro ta'sir qilishini simulyatsiya qiladigan testlar yozing.
- Amalga oshirish tafsilotlarini testlashdan saqlaning: Komponentlaringizning ichki ishlashini testlamang. Buning o'rniga, kuzatiladigan xatti-harakatlarga e'tibor qarating.
- Aniq va qisqa testlar yozing: Testlaringizni tushunish va qo'llab-quvvatlash oson bo'lsin.
- Mazmunli test nomlaridan foydalaning: Testlanayotgan xatti-harakatni aniq tasvirlaydigan test nomlarini tanlang.
- Testlarni izolyatsiya qiling: Testlar orasidagi bog'liqliklardan saqlaning. Har bir test mustaqil va o'z-o'zidan yetarli bo'lishi kerak.
- Chekka holatlarni testlang: Faqat "baxtli yo'l"ni (happy path) testlamang. Chekka holatlar va xatolik shartlarini ham testlashga ishonch hosil qiling.
- Kod yozishdan oldin testlar yozing: Kodingizni yozishdan oldin testlar yozish uchun Test-Driven Development (TDD) dan foydalanishni o'ylab ko'ring.
- "AAA" naqshiga rioya qiling: Arrange (Tartibga solish), Act (Harakat qilish), Assert (Tasdiqlash). Bu naqsh testlaringizni tuzishga va ularni o'qilishi osonroq qilishga yordam beradi.
- Testlaringizni tez saqlang: Sekin testlar dasturchilarni ularni tez-tez ishga tushirishdan to'xtatishi mumkin. Tarmoq so'rovlarini mock qilish va DOM manipulyatsiyasi miqdorini minimallashtirish orqali testlaringizni tezlik uchun optimallashtiring.
- Tavsiflovchi xato xabarlaridan foydalaning: Tasdiqlashlar muvaffaqiyatsiz bo'lganda, xato xabarlari muvaffaqiyatsizlik sababini tezda aniqlash uchun yetarli ma'lumot berishi kerak.
Xulosa
React Testing Library React ilovalaringiz uchun samarali, qo'llab-quvvatlanadigan va foydalanuvchiga yo'naltirilgan testlar yozish uchun kuchli vositadir. Ushbu qo'llanmada keltirilgan tamoyillar va texnikalarga amal qilish orqali siz foydalanuvchilaringiz ehtiyojlarini qondiradigan mustahkam va ishonchli ilovalar yarata olasiz. Foydalanuvchi nuqtai nazaridan testlashga e'tibor qaratishni, amalga oshirish tafsilotlarini testlashdan qochishni va aniq va qisqa testlar yozishni unutmang. RTL'ni qabul qilish va eng yaxshi amaliyotlarni qo'llash orqali siz joylashuvingiz yoki global auditoriyangizning o'ziga xos talablaridan qat'i nazar, React loyihalaringizning sifati va qo'llab-quvvatlanishini sezilarli darajada yaxshilashingiz mumkin.