O'zbek

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:

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

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:

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:

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:

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:

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:

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:

Samarali Testlarni Yozish uchun Eng Yaxshi Amaliyotlar

RTL bilan testlar yozishda amal qilish kerak bo'lgan ba'zi eng yaxshi amaliyotlar:

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.