বাংলা

এই সম্পূর্ণ গাইডের মাধ্যমে রিঅ্যাক্ট টেস্টিং লাইব্রেরি (RTL) আয়ত্ত করুন। সেরা অনুশীলন এবং বাস্তব উদাহরণসহ আপনার রিঅ্যাক্ট অ্যাপ্লিকেশনের জন্য কার্যকর, রক্ষণাবেক্ষণযোগ্য এবং ব্যবহারকারী-কেন্দ্রিক পরীক্ষা লিখতে শিখুন।

রিঅ্যাক্ট টেস্টিং লাইব্রেরি: একটি বিস্তারিত গাইড

আজকের দ্রুতগতির ওয়েব ডেভেলপমেন্টের জগতে, আপনার রিঅ্যাক্ট অ্যাপ্লিকেশনগুলির গুণমান এবং নির্ভরযোগ্যতা নিশ্চিত করা অপরিহার্য। রিঅ্যাক্ট টেস্টিং লাইব্রেরি (RTL) ব্যবহারকারীর দৃষ্টিকোণ থেকে পরীক্ষা লেখার জন্য একটি জনপ্রিয় এবং কার্যকর সমাধান হিসেবে আবির্ভূত হয়েছে। এই গাইডটি RTL-এর একটি সম্পূর্ণ চিত্র তুলে ধরে, মৌলিক ধারণা থেকে শুরু করে উন্নত কৌশল পর্যন্ত সবকিছু কভার করে, যা আপনাকে শক্তিশালী এবং রক্ষণাবেক্ষণযোগ্য রিঅ্যাক্ট অ্যাপ্লিকেশন তৈরি করতে সক্ষম করবে।

কেন রিঅ্যাক্ট টেস্টিং লাইব্রেরি বেছে নেবেন?

প্রচলিত টেস্টিং পদ্ধতিগুলো প্রায়শই ইমপ্লিমেন্টেশন ডিটেইলসের উপর নির্ভর করে, যা সামান্য কোড পরিবর্তনেই টেস্টগুলোকে ভঙ্গুর করে তোলে। আরটিএল, অন্যদিকে, আপনাকে আপনার কম্পোনেন্টগুলোকে এমনভাবে পরীক্ষা করতে উৎসাহিত করে যেভাবে একজন ব্যবহারকারী সেগুলোর সাথে ইন্টারঅ্যাক্ট করবে, অর্থাৎ ব্যবহারকারী যা দেখে এবং অনুভব করে তার উপর ফোকাস করে। এই পদ্ধতির বেশ কিছু মূল সুবিধা রয়েছে:

আপনার টেস্টিং এনভায়রনমেন্ট সেট আপ করা

আপনি RTL ব্যবহার শুরু করার আগে, আপনাকে আপনার টেস্টিং এনভায়রনমেন্ট সেট আপ করতে হবে। এর জন্য সাধারণত প্রয়োজনীয় ডিপেন্ডেন্সি ইনস্টল করা এবং আপনার টেস্টিং ফ্রেমওয়ার্ক কনফিগার করা জড়িত।

পূর্বশর্ত

ইনস্টলেশন

npm বা yarn ব্যবহার করে নিম্নলিখিত প্যাকেজগুলো ইনস্টল করুন:

npm install --save-dev @testing-library/react @testing-library/jest-dom jest babel-jest @babel/preset-env @babel/preset-react

অথবা, yarn ব্যবহার করে:

yarn add --dev @testing-library/react @testing-library/jest-dom jest babel-jest @babel/preset-env @babel/preset-react

প্যাকেজগুলোর ব্যাখ্যা:

কনফিগারেশন

আপনার প্রজেক্টের রুটে নিম্নলিখিত বিষয়বস্তু সহ একটি `babel.config.js` ফাইল তৈরি করুন:

module.exports = {
  presets: ['@babel/preset-env', '@babel/preset-react'],
};

একটি টেস্ট স্ক্রিপ্ট অন্তর্ভুক্ত করার জন্য আপনার `package.json` ফাইল আপডেট করুন:

{
  "scripts": {
    "test": "jest"
  }
}

জেস্ট কনফিগার করার জন্য আপনার প্রজেক্টের রুটে একটি `jest.config.js` ফাইল তৈরি করুন। একটি ন্যূনতম কনফিগারেশন এইরকম হতে পারে:

module.exports = {
  testEnvironment: 'jsdom',
  setupFilesAfterEnv: ['/src/setupTests.js'],
};

নিম্নলিখিত বিষয়বস্তু সহ একটি `src/setupTests.js` ফাইল তৈরি করুন। এটি নিশ্চিত করে যে জেস্ট DOM ম্যাচারগুলো আপনার সমস্ত টেস্টে উপলব্ধ থাকবে:

import '@testing-library/jest-dom/extend-expect';

আপনার প্রথম টেস্ট লেখা

আসুন একটি সহজ উদাহরণ দিয়ে শুরু করা যাক। ধরুন আপনার একটি রিঅ্যাক্ট কম্পোনেন্ট আছে যা একটি অভিবাদন বার্তা প্রদর্শন করে:

// src/components/Greeting.js
import React from 'react';

function Greeting({ name }) {
  return <h1>Hello, {name}!</h1>;
}

export default Greeting;

এখন, আসুন এই কম্পোনেন্টের জন্য একটি টেস্ট লিখি:

// src/components/Greeting.test.js
import React from 'react';
import { render, screen } from '@testing-library/react';
import Greeting from './Greeting';

test('একটি অভিবাদন বার্তা রেন্ডার করে', () => {
  render(<Greeting name="World" />);
  const greetingElement = screen.getByText(/Hello, World!/i);
  expect(greetingElement).toBeInTheDocument();
});

ব্যাখ্যা:

টেস্ট চালানোর জন্য, আপনার টার্মিনালে নিম্নলিখিত কমান্ডটি চালান:

npm test

যদি সবকিছু সঠিকভাবে কনফিগার করা থাকে, তাহলে টেস্টটি পাস করা উচিত।

সাধারণ RTL কোয়েরি

RTL DOM-এ এলিমেন্ট খোঁজার জন্য বিভিন্ন কোয়েরি মেথড সরবরাহ করে। এই কোয়েরিগুলো ডিজাইন করা হয়েছে ব্যবহারকারীরা কীভাবে আপনার অ্যাপ্লিকেশনের সাথে ইন্টারঅ্যাক্ট করে তা অনুকরণ করার জন্য।

`getByRole`

এই কোয়েরিটি একটি এলিমেন্টকে তার ARIA role দ্বারা খুঁজে বের করে। যখনই সম্ভব `getByRole` ব্যবহার করা একটি ভাল অভ্যাস, কারণ এটি অ্যাক্সেসিবিলিটি উন্নত করে এবং নিশ্চিত করে যে আপনার টেস্টগুলো অন্তর্নিহিত DOM কাঠামোর পরিবর্তনেও স্থিতিশীল থাকে।

<button role="button">Click me</button>
const buttonElement = screen.getByRole('button');
expect(buttonElement).toBeInTheDocument();

`getByLabelText`

এই কোয়েরিটি একটি এলিমেন্টকে তার সংশ্লিষ্ট লেবেলের টেক্সট দ্বারা খুঁজে বের করে। এটি ফর্ম এলিমেন্ট পরীক্ষা করার জন্য দরকারী।

<label htmlFor="name">Name:</label>
<input type="text" id="name" />
const nameInputElement = screen.getByLabelText('Name:');
expect(nameInputElement).toBeInTheDocument();

`getByPlaceholderText`

এই কোয়েরিটি একটি এলিমেন্টকে তার placeholder টেক্সট দ্বারা খুঁজে বের করে।

<input type="text" placeholder="Enter your email" />
const emailInputElement = screen.getByPlaceholderText('Enter your email');
expect(emailInputElement).toBeInTheDocument();

`getByAltText`

এই কোয়েরিটি একটি ইমেজ এলিমেন্টকে তার alt টেক্সট দ্বারা খুঁজে বের করে। অ্যাক্সেসিবিলিটি নিশ্চিত করার জন্য সমস্ত ইমেজের জন্য অর্থপূর্ণ alt টেক্সট প্রদান করা গুরুত্বপূর্ণ।

<img src="logo.png" alt="Company Logo" />
const logoImageElement = screen.getByAltText('Company Logo');
expect(logoImageElement).toBeInTheDocument();

`getByTitle`

এই কোয়েরিটি একটি এলিমেন্টকে তার title অ্যাট্রিবিউট দ্বারা খুঁজে বের করে।

<span title="Close">X</span>
const closeElement = screen.getByTitle('Close');
expect(closeElement).toBeInTheDocument();

`getByDisplayValue`

এই কোয়েরিটি একটি এলিমেন্টকে তার display value দ্বারা খুঁজে বের করে। এটি আগে থেকে পূরণ করা মান সহ ফর্ম ইনপুট পরীক্ষা করার জন্য দরকারী।

<input type="text" value="Initial Value" />
const inputElement = screen.getByDisplayValue('Initial Value');
expect(inputElement).toBeInTheDocument();

`getAllBy*` কোয়েরি

`getBy*` কোয়েরি ছাড়াও, RTL `getAllBy*` কোয়েরিও সরবরাহ করে, যা মিলে যাওয়া এলিমেন্টগুলির একটি অ্যারে রিটার্ন করে। যখন আপনাকে অ্যাসার্ট করতে হয় যে একই বৈশিষ্ট্যের একাধিক এলিমেন্ট DOM-এ উপস্থিত আছে তখন এগুলি দরকারী।

<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
const listItems = screen.getAllByRole('listitem');
expect(listItems).toHaveLength(3);

`queryBy*` কোয়েরি

`queryBy*` কোয়েরিগুলো `getBy*` কোয়েরিগুলোর মতোই, কিন্তু যদি কোনো মিলে যাওয়া এলিমেন্ট না পাওয়া যায় তবে এরা একটি ত্রুটি নিক্ষেপ করার পরিবর্তে `null` রিটার্ন করে। যখন আপনি অ্যাসার্ট করতে চান যে একটি এলিমেন্ট DOM-এ উপস্থিত *নেই* তখন এটি দরকারী।

const missingElement = screen.queryByText('Non-existent text');
expect(missingElement).toBeNull();

`findBy*` কোয়েরি

`findBy*` কোয়েরিগুলো `getBy*` কোয়েরিগুলোর অ্যাসিঙ্ক্রোনাস সংস্করণ। তারা একটি Promise রিটার্ন করে যা মিলে যাওয়া এলিমেন্টটি পাওয়া গেলে সমাধান হয়। এগুলি অ্যাসিঙ্ক্রোনাস অপারেশন পরীক্ষা করার জন্য দরকারী, যেমন একটি API থেকে ডেটা আনা।

// একটি অ্যাসিঙ্ক্রোনাস ডেটা ফেচ সিমুলেট করা হচ্ছে
const fetchData = () => new Promise(resolve => {
  setTimeout(() => resolve('Data Loaded!'), 1000);
});

function MyComponent() {
  const [data, setData] = React.useState(null);

  React.useEffect(() => {
    fetchData().then(setData);
  }, []);

  return <div>{data}</div>;
}
test('অ্যাসিঙ্ক্রোনাসভাবে ডেটা লোড করে', async () => {
  render(<MyComponent />);
  const dataElement = await screen.findByText('Data Loaded!');
  expect(dataElement).toBeInTheDocument();
});

ব্যবহারকারীর ইন্টারঅ্যাকশন সিমুলেট করা

RTL ব্যবহারকারীর ইন্টারঅ্যাকশন সিমুলেট করার জন্য `fireEvent` এবং `userEvent` API সরবরাহ করে, যেমন বোতামে ক্লিক করা, ইনপুট ফিল্ডে টাইপ করা এবং ফর্ম জমা দেওয়া।

`fireEvent`

`fireEvent` আপনাকে প্রোগ্রাম্যাটিকভাবে DOM ইভেন্ট ট্রিগার করতে দেয়। এটি একটি নিম্ন-স্তরের API যা আপনাকে ফায়ার করা ইভেন্টগুলির উপর সূক্ষ্ম-নিয়ন্ত্রণ দেয়।

<button onClick={() => alert('Button clicked!')}>Click me</button>
import { fireEvent } from '@testing-library/react';

test('একটি বোতাম ক্লিক সিমুলেট করে', () => {
  const alertMock = jest.spyOn(window, 'alert').mockImplementation(() => {});
  render(<button onClick={() => alert('Button clicked!')}>Click me</button>);
  const buttonElement = screen.getByRole('button');
  fireEvent.click(buttonElement);
  expect(alertMock).toHaveBeenCalledTimes(1);
  alertMock.mockRestore();
});

`userEvent`

`userEvent` একটি উচ্চ-স্তরের API যা ব্যবহারকারীর ইন্টারঅ্যাকশনকে আরও বাস্তবসম্মতভাবে সিমুলেট করে। এটি ফোকাস ম্যানেজমেন্ট এবং ইভেন্ট অর্ডারিংয়ের মতো বিবরণ পরিচালনা করে, যা আপনার টেস্টগুলোকে আরও শক্তিশালী এবং কম ভঙ্গুর করে তোলে।

<input type="text" onChange={e => console.log(e.target.value)} />
import userEvent from '@testing-library/user-event';

test('একটি ইনপুট ফিল্ডে টাইপ করা সিমুলেট করে', () => {
  const inputElement = screen.getByRole('textbox');
  userEvent.type(inputElement, 'Hello, world!');
  expect(inputElement).toHaveValue('Hello, world!');
});

অ্যাসিঙ্ক্রোনাস কোড টেস্টিং

অনেক রিঅ্যাক্ট অ্যাপ্লিকেশন অ্যাসিঙ্ক্রোনাস অপারেশন জড়িত, যেমন একটি API থেকে ডেটা আনা। RTL অ্যাসিঙ্ক্রোনাস কোড পরীক্ষা করার জন্য বেশ কিছু সরঞ্জাম সরবরাহ করে।

`waitFor`

`waitFor` আপনাকে একটি অ্যাসারশন করার আগে একটি শর্ত সত্য হওয়ার জন্য অপেক্ষা করতে দেয়। এটি অ্যাসিঙ্ক্রোনাস অপারেশন পরীক্ষা করার জন্য দরকারী যা সম্পূর্ণ হতে কিছু সময় নেয়।

function MyComponent() {
  const [data, setData] = React.useState(null);

  React.useEffect(() => {
    setTimeout(() => {
      setData('Data loaded!');
    }, 1000);
  }, []);

  return <div>{data}</div>;
}
import { waitFor } from '@testing-library/react';

test('ডেটা লোড হওয়ার জন্য অপেক্ষা করে', async () => {
  render(<MyComponent />);
  await waitFor(() => screen.getByText('Data loaded!'));
  const dataElement = screen.getByText('Data loaded!');
  expect(dataElement).toBeInTheDocument();
});

`findBy*` কোয়েরি

যেমন আগে উল্লেখ করা হয়েছে, `findBy*` কোয়েরিগুলো অ্যাসিঙ্ক্রোনাস এবং একটি Promise রিটার্ন করে যা মিলে যাওয়া এলিমেন্টটি পাওয়া গেলে সমাধান হয়। এগুলি অ্যাসিঙ্ক্রোনাস অপারেশন পরীক্ষা করার জন্য দরকারী যা DOM-এ পরিবর্তনের কারণ হয়।

হুক টেস্টিং

রিঅ্যাক্ট হুক হলো পুনঃব্যবহারযোগ্য ফাংশন যা স্টেটফুল লজিককে এনক্যাপসুলেট করে। RTL কাস্টম হুকগুলোকে বিচ্ছিন্নভাবে পরীক্ষা করার জন্য `@testing-library/react-hooks` থেকে `renderHook` ইউটিলিটি প্রদান করে (যা v17 থেকে `@testing-library/react`-এর পক্ষে অবচয়িত হয়েছে)।

// 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('কাউন্টার বৃদ্ধি করে', () => {
  const { result } = renderHook(() => useCounter());

  act(() => {
    result.current.increment();
  });

  expect(result.current.count).toBe(1);
});

ব্যাখ্যা:

উন্নত টেস্টিং কৌশল

আপনি RTL-এর মূল বিষয়গুলো আয়ত্ত করার পরে, আপনি আপনার টেস্টের গুণমান এবং রক্ষণাবেক্ষণযোগ্যতা উন্নত করতে আরও উন্নত টেস্টিং কৌশলগুলো অন্বেষণ করতে পারেন।

মডিউল মক করা

কখনও কখনও, আপনার কম্পোনেন্টগুলোকে বিচ্ছিন্ন করতে এবং পরীক্ষার সময় তাদের আচরণ নিয়ন্ত্রণ করতে আপনাকে বাহ্যিক মডিউল বা ডিপেন্ডেন্সি মক করতে হতে পারে। জেস্ট এই উদ্দেশ্যে একটি শক্তিশালী মকিং API সরবরাহ করে।

// 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}</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 থেকে ডেটা ফেচ করে', async () => {
  dataService.fetchData.mockResolvedValue({ message: 'Mocked data!' });

  render(<MyComponent />);

  await waitFor(() => screen.getByText('Mocked data!'));

  expect(screen.getByText('Mocked data!')).toBeInTheDocument();
  expect(dataService.fetchData).toHaveBeenCalledTimes(1);
});

ব্যাখ্যা:

কনটেক্সট প্রোভাইডার

যদি আপনার কম্পোনেন্ট একটি কনটেক্সট প্রোভাইডারের উপর নির্ভর করে, তাহলে পরীক্ষার সময় আপনাকে আপনার কম্পোনেন্টকে সেই প্রোভাইডারে মোড়াতে হবে। এটি নিশ্চিত করে যে কম্পোনেন্টটির কনটেক্সট মানগুলিতে অ্যাক্সেস রয়েছে।

// 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>Current theme: {theme}</p>
      <button onClick={toggleTheme}>Toggle Theme</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('থিম টগল করে', () => {
  render(
    <ThemeProvider>
      <MyComponent />
    </ThemeProvider>
  );

  const themeParagraph = screen.getByText(/Current theme: light/i);
  const toggleButton = screen.getByRole('button', { name: /Toggle Theme/i });

  expect(themeParagraph).toBeInTheDocument();

  fireEvent.click(toggleButton);

  expect(screen.getByText(/Current theme: dark/i)).toBeInTheDocument();
});

ব্যাখ্যা:

রাউটারের সাথে টেস্টিং

রিঅ্যাক্ট রাউটার ব্যবহার করে এমন কম্পোনেন্ট পরীক্ষা করার সময়, আপনাকে একটি মক রাউটার কনটেক্সট সরবরাহ করতে হবে। আপনি `react-router-dom` থেকে `MemoryRouter` কম্পোনেন্ট ব্যবহার করে এটি অর্জন করতে পারেন।

// src/components/MyComponent.js
import React from 'react';
import { Link } from 'react-router-dom';

function MyComponent() {
  return (
    <div>
      <Link to="/about">Go to About Page</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('সম্পর্কে পেজের একটি লিঙ্ক রেন্ডার করে', () => {
  render(
    <MemoryRouter>
      <MyComponent />
    </MemoryRouter>
  );

  const linkElement = screen.getByRole('link', { name: /Go to About Page/i });
  expect(linkElement).toBeInTheDocument();
  expect(linkElement).toHaveAttribute('href', '/about');
});

ব্যাখ্যা:

কার্যকর টেস্ট লেখার জন্য সেরা অনুশীলন

RTL দিয়ে টেস্ট লেখার সময় অনুসরণ করার জন্য এখানে কিছু সেরা অনুশীলন রয়েছে:

উপসংহার

রিঅ্যাক্ট টেস্টিং লাইব্রেরি আপনার রিঅ্যাক্ট অ্যাপ্লিকেশনগুলির জন্য কার্যকর, রক্ষণাবেক্ষণযোগ্য এবং ব্যবহারকারী-কেন্দ্রিক পরীক্ষা লেখার জন্য একটি শক্তিশালী টুল। এই গাইডে বর্ণিত নীতি এবং কৌশলগুলো অনুসরণ করে, আপনি শক্তিশালী এবং নির্ভরযোগ্য অ্যাপ্লিকেশন তৈরি করতে পারেন যা আপনার ব্যবহারকারীদের চাহিদা পূরণ করে। ব্যবহারকারীর দৃষ্টিকোণ থেকে পরীক্ষা করার উপর ফোকাস করতে, ইমপ্লিমেন্টেশন ডিটেইলস পরীক্ষা করা এড়াতে এবং পরিষ্কার ও সংক্ষিপ্ত টেস্ট লিখতে মনে রাখবেন। RTL গ্রহণ করে এবং সেরা অনুশীলনগুলো অবলম্বন করে, আপনি আপনার অবস্থান বা আপনার বিশ্বব্যাপী দর্শকদের নির্দিষ্ট প্রয়োজনীয়তা নির্বিশেষে আপনার রিঅ্যাক্ট প্রজেক্টগুলোর গুণমান এবং রক্ষণাবেক্ষণযোগ্যতা উল্লেখযোগ্যভাবে উন্নত করতে পারেন।