Redux ব্যবহার করার সময় আপনার অ্যাপ্লিকেশনের বিভিন্ন অংশ যেমন reducers, actions, এবং store-এর সঠিক কার্যকারিতা নিশ্চিত করার জন্য টেস্টিং খুবই গুরুত্বপূর্ণ। Redux অ্যাপ্লিকেশন ডেভেলপ করার সময় আপনাকে এসব অংশের ইউনিট টেস্টিং করতে হবে যাতে নিশ্চিত হওয়া যায় যে আপনার অ্যাপ্লিকেশন সঠিকভাবে কাজ করছে এবং কোনো বাগ নেই।
Redux অ্যাপ্লিকেশনের টেস্টিংয়ে সাধারণত Jest এবং React Testing Library এর মতো টেস্টিং টুলস ব্যবহৃত হয়। এই টুলসগুলো দিয়ে আপনি Redux স্টোর, reducers এবং actions এর কার্যকারিতা পরীক্ষা করতে পারেন।
Redux টেস্টিং: কেন প্রয়োজন?
Redux অ্যাপ্লিকেশনে টেস্টিং অত্যন্ত গুরুত্বপূর্ণ কারণ:
- স্টেট এবং লজিকের নির্ভুলতা: রিডিউসার এবং অ্যাকশন সঠিকভাবে কাজ করছে কিনা তা নিশ্চিত করতে টেস্টিং প্রয়োজন।
- ডিবাগিং সহজ: টেস্টিং অ্যাপ্লিকেশন ডেভেলপমেন্টের সময় ত্রুটি শনাক্ত করতে সাহায্য করে।
- কোড কভারেজ: টেস্টিং করার মাধ্যমে কোডের কভারেজ বাড়ানো যায়, যা দীর্ঘমেয়াদী প্রকল্পের জন্য উপকারী।
- রক্ষণাবেক্ষণ সহজ: যখন অ্যাপ্লিকেশনে নতুন ফিচার যোগ করা হয়, তখন টেস্টিং আগের ফিচারের কার্যকারিতা রক্ষা করে।
Redux টেস্টিং: সাধারণ টেস্টিং কৌশল
Redux অ্যাপ্লিকেশন টেস্টিংয়ে সাধারণত reducers, actions, এবং store এর টেস্টিং করা হয়।
১. Reducer টেস্টিং
Redux রিডিউসার হচ্ছে ফাংশন, যা অ্যাকশন পাওয়ার পর স্টেট পরিবর্তন করে। রিডিউসারের টেস্টিং করা বেশ সহজ। সাধারণত, আপনি বিভিন্ন অ্যাকশন টাইপ দিয়ে রিডিউসারকে চালিয়ে তার আউটপুট পরীক্ষা করবেন।
Reducer টেস্টিং উদাহরণ
ধরা যাক, আমাদের একটি counterReducer আছে যা একটি increment এবং decrement অ্যাকশন নিয়ে কাজ করে।
// counterSlice.js
const initialState = { value: 0 };
const counterReducer = (state = initialState, action) => {
switch (action.type) {
case 'counter/increment':
return { value: state.value + 1 };
case 'counter/decrement':
return { value: state.value - 1 };
default:
return state;
}
};
export default counterReducer;
এখন, আমরা Jest দিয়ে এই রিডিউসারটি টেস্ট করবো।
Reducer টেস্টিং কোড
import counterReducer from './counterSlice';
describe('counterReducer', () => {
it('should handle increment action', () => {
const initialState = { value: 0 };
const action = { type: 'counter/increment' };
const result = counterReducer(initialState, action);
expect(result.value).toBe(1);
});
it('should handle decrement action', () => {
const initialState = { value: 1 };
const action = { type: 'counter/decrement' };
const result = counterReducer(initialState, action);
expect(result.value).toBe(0);
});
});
এখানে:
incrementএবংdecrementঅ্যাকশনের জন্য রিডিউসারের আউটপুট যাচাই করা হচ্ছে।- প্রত্যেকটি অ্যাকশনের পরে স্টেট পরিবর্তন সঠিক কিনা তা যাচাই করা হচ্ছে।
২. Action Creators টেস্টিং
Redux অ্যাকশন ক্রিয়েটররা সাধারণত অ্যাকশন অবজেক্ট তৈরি করে এবং এগুলির মাধ্যমে স্টোরে স্টেট পরিবর্তিত হয়। অ্যাকশন ক্রিয়েটরের টেস্টিং করা খুবই গুরুত্বপূর্ণ, কারণ এটি নিশ্চিত করে যে অ্যাকশন সঠিকভাবে তৈরি হচ্ছে।
Action Creator টেস্টিং উদাহরণ
// counterSlice.js
export const increment = () => ({
type: 'counter/increment'
});
export const decrement = () => ({
type: 'counter/decrement'
});
Action Creator টেস্টিং কোড
import { increment, decrement } from './counterSlice';
describe('counter actions', () => {
it('should create an increment action', () => {
const action = increment();
expect(action).toEqual({ type: 'counter/increment' });
});
it('should create a decrement action', () => {
const action = decrement();
expect(action).toEqual({ type: 'counter/decrement' });
});
});
এখানে:
incrementএবংdecrementঅ্যাকশন ক্রিয়েটরের আউটপুট যাচাই করা হচ্ছে।- প্রত্যেকটি অ্যাকশন সঠিক টাইপ এবং ডেটা সহ তৈরি হচ্ছে কিনা তা পরীক্ষা করা হচ্ছে।
৩. Store টেস্টিং
Redux স্টোরের টেস্টিং সাধারণত কমপ্লেক্স অ্যাপ্লিকেশনগুলির জন্য প্রয়োজন হতে পারে যেখানে আপনি চান যে স্টোরে নির্দিষ্ট অ্যাকশন বা স্টেট পরিবর্তন ট্র্যাক করা হোক। স্টোর টেস্টিংয়ে আপনি সঠিক রিডিউসার এবং মিডলওয়্যার প্রয়োগ করেছে কিনা তা পরীক্ষা করবেন।
Store টেস্টিং উদাহরণ
import { configureStore } from '@reduxjs/toolkit';
import counterReducer from './counterSlice';
describe('Redux Store', () => {
let store;
beforeEach(() => {
store = configureStore({
reducer: {
counter: counterReducer,
},
});
});
it('should have an initial state of 0', () => {
const state = store.getState();
expect(state.counter.value).toBe(0);
});
it('should increment the counter value', () => {
store.dispatch({ type: 'counter/increment' });
const state = store.getState();
expect(state.counter.value).toBe(1);
});
it('should decrement the counter value', () => {
store.dispatch({ type: 'counter/increment' });
store.dispatch({ type: 'counter/decrement' });
const state = store.getState();
expect(state.counter.value).toBe(0);
});
});
এখানে:
configureStoreব্যবহার করে স্টোর কনফিগার করা হচ্ছে এবং তারপর অ্যাকশন ডিসপ্যাচ করে স্টেটের পরিবর্তন যাচাই করা হচ্ছে।
Testing Redux Middleware (Redux Thunk/Saga)
যদি আপনার অ্যাপ্লিকেশন Redux Thunk বা Redux Saga ব্যবহার করে অ্যাসিনক্রোনাস অ্যাকশন পরিচালনা করে, তবে আপনাকে এই মিডলওয়্যারগুলোও টেস্ট করতে হবে। Redux Thunk এর জন্য আপনি mockStore ব্যবহার করতে পারেন, যা আপনার অ্যাকশনগুলো ট্র্যাক করতে সাহায্য করবে।
Redux Thunk টেস্টিং উদাহরণ
import configureStore from 'redux-mock-store';
import thunk from 'redux-thunk';
import { incrementAsync } from './counterSlice';
const mockStore = configureStore([thunk]);
describe('incrementAsync action', () => {
it('should dispatch increment action asynchronously', () => {
const store = mockStore({ counter: { value: 0 } });
return store.dispatch(incrementAsync()).then(() => {
const actions = store.getActions();
expect(actions[0]).toEqual({ type: 'counter/increment' });
});
});
});
এখানে:
incrementAsyncঅ্যাকশনটি একটি অ্যাসিনক্রোনাস অ্যাকশন।mockStoreব্যবহার করে আমরা অ্যাকশন ডিসপ্যাচ করার পর সেগুলি যাচাই করতে পারি।
সারাংশ
Redux অ্যাপ্লিকেশন টেস্টিং অত্যন্ত গুরুত্বপূর্ণ কারণ এটি নিশ্চিত করে যে আপনার স্টেট ম্যানেজমেন্ট সঠিকভাবে কাজ করছে এবং আপনার অ্যাপ্লিকেশনে কোনো ত্রুটি নেই। Redux এর reducers, actions, এবং store টেস্টিংয়ের জন্য আপনি Jest ব্যবহার করতে পারেন এবং যদি অ্যাসিনক্রোনাস অ্যাকশন থাকে তবে mockStore বা Redux Thunk এর মতো মিডলওয়্যার ব্যবহারের মাধ্যমে আপনি সেগুলোর কার্যকারিতা পরীক্ষা করতে পারবেন। এর মাধ্যমে আপনি আপনার Redux অ্যাপ্লিকেশনকে আরও নির্ভুল, স্থিতিশীল এবং রক্ষণাবেক্ষণযোগ্য রাখতে পারবেন।
Redux অ্যাপ্লিকেশন উন্নয়ন করতে গিয়ে কোডের Unit Testing এবং Integration Testing খুবই গুরুত্বপূর্ণ ভূমিকা পালন করে। এটি নিশ্চিত করে যে, আপনার স্টোর, রিডিউসার, অ্যাকশন, এবং অন্যান্য Redux উপাদানগুলো সঠিকভাবে কাজ করছে এবং অ্যাপ্লিকেশনটি নির্ভরযোগ্যভাবে কার্যকরী হচ্ছে। এই টেস্টিং প্রক্রিয়া নিশ্চিত করে যে সিস্টেমের প্রতিটি অংশ অখণ্ড এবং কর্মক্ষম।
এখানে আমরা আলোচনা করবো Unit Testing এবং Integration Testing এর মধ্যে পার্থক্য এবং Redux অ্যাপ্লিকেশন টেস্ট করার জন্য কীভাবে এই দুটি পদ্ধতি ব্যবহার করা যেতে পারে।
Unit Testing (ইউনিট টেস্টিং)
Unit Testing হচ্ছে কোডের একটি ছোট্ট একক অংশ (যেমন ফাংশন বা মেথড) টেস্ট করা। Redux-এ, এটি সাধারণত অ্যাকশন ক্রিয়েটর, রিডিউসার এবং স্টোরের অন্যান্য ছোট ফাংশন এর জন্য করা হয়। ইউনিট টেস্টের মাধ্যমে আপনি নিশ্চিত করতে পারেন যে একটি নির্দিষ্ট একক অংশ ঠিকভাবে কাজ করছে।
Redux Unit Testing এর জন্য জনপ্রিয় টুল:
- Jest: JavaScript টেস্টিং ফ্রেমওয়ার্ক, যা ব্যাপকভাবে ব্যবহৃত হয়।
- Testing Library: UI এবং কম্পোনেন্ট টেস্টিংয়ের জন্য ব্যবহৃত হয়।
উদাহরণ: Reducer Unit Test
Redux রিডিউসার টেস্ট করার জন্য, আপনি Jest ব্যবহার করতে পারেন। উদাহরণস্বরূপ, যদি আপনার একটি সিম্পল রিডিউসার থাকে যা কাউন্ট বাড়ায় বা কমায়:
// actions.js
export const increment = () => ({
type: 'INCREMENT',
});
export const decrement = () => ({
type: 'DECREMENT',
});
// reducer.js
const initialState = {
count: 0,
};
const counterReducer = (state = initialState, action) => {
switch (action.type) {
case 'INCREMENT':
return { ...state, count: state.count + 1 };
case 'DECREMENT':
return { ...state, count: state.count - 1 };
default:
return state;
}
};
export default counterReducer;
এখন, এই রিডিউসারটি টেস্ট করা যাক:
// reducer.test.js
import counterReducer from './reducer';
import { increment, decrement } from './actions';
describe('counterReducer', () => {
it('should increment count', () => {
const initialState = { count: 0 };
const action = increment();
const nextState = counterReducer(initialState, action);
expect(nextState.count).toBe(1);
});
it('should decrement count', () => {
const initialState = { count: 1 };
const action = decrement();
const nextState = counterReducer(initialState, action);
expect(nextState.count).toBe(0);
});
});
এখানে:
incrementএবংdecrementঅ্যাকশনগুলো টেস্ট করা হচ্ছে।counterReducerরিডিউসারের মাধ্যমে স্টেট পরিবর্তন চেক করা হচ্ছে।
Integration Testing (ইন্টিগ্রেশন টেস্টিং)
Integration Testing হচ্ছে বিভিন্ন অংশের সমন্বয়ে পুরো সিস্টেমের কাজ পরীক্ষা করা। Redux অ্যাপ্লিকেশনে, এটি মূলত স্টোর, রিডিউসার এবং অ্যাকশন একসাথে একত্রে পরীক্ষা করা হয়, যাতে নিশ্চিত করা যায় যে বিভিন্ন অংশ একে অপরের সাথে সঠিকভাবে কাজ করছে।
ইন্টিগ্রেশন টেস্টিং-এ অ্যাপ্লিকেশনের একাধিক ফিচার বা উপাদান একত্রে টেস্ট করা হয়। যেমন স্টোরের মাধ্যমে অ্যাকশন ডিসপ্যাচ করা এবং রিডিউসারগুলো ঠিকভাবে স্টেট পরিবর্তন করছে কিনা তা পরীক্ষা করা।
Redux Integration Testing উদাহরণ
// store.js
import { configureStore } from '@reduxjs/toolkit';
import counterReducer from './reducer';
const store = configureStore({
reducer: {
counter: counterReducer,
},
});
export default store;
// component.js
import React from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { increment } from './actions';
const Counter = () => {
const dispatch = useDispatch();
const count = useSelector((state) => state.counter.count);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => dispatch(increment())}>Increment</button>
</div>
);
};
export default Counter;
এখন, আমরা এই কম্পোনেন্টের ইন্টিগ্রেশন টেস্ট করবো যেখানে আমরা Redux স্টোরকে সম্পূর্ণভাবে অন্তর্ভুক্ত করবো:
// component.test.js
import React from 'react';
import { render, fireEvent } from '@testing-library/react';
import { Provider } from 'react-redux';
import store from './store';
import Counter from './component';
describe('Counter component', () => {
it('should increment count when button is clicked', () => {
const { getByText } = render(
<Provider store={store}>
<Counter />
</Provider>
);
const button = getByText('Increment');
fireEvent.click(button);
expect(getByText('Count: 1')).toBeInTheDocument();
});
});
এখানে:
Providerকম্পোনেন্ট ব্যবহার করে Redux স্টোর কম্পোনেন্টে যুক্ত করা হয়েছে।fireEvent.click()ব্যবহার করে বাটনে ক্লিক করা হচ্ছে এবংCountমানটি সঠিকভাবে পরিবর্তিত হচ্ছে কিনা তা চেক করা হচ্ছে।
Redux টেস্টিং এর জন্য অন্যান্য গুরুত্বপূর্ণ টিপস
Async Actions Testing: যদি আপনার অ্যাকশন অ্যাসিনক্রোনাস (যেমন API কল) হয়, তাহলে
redux-thunkবা অন্য Middleware ব্যবহার করলে আপনাকে অ্যাসিনক্রোনাস টেস্টিংয়ের জন্যjestএরasync/awaitফিচার ব্যবহার করতে হবে।উদাহরণ:
import { fetchUserData } from './actions'; import { render, screen } from '@testing-library/react'; import store from './store'; import { Provider } from 'react-redux'; test('fetches and displays user data', async () => { const { dispatch } = store; await dispatch(fetchUserData(1)); // অ্যাসিনক্রোনাস অ্যাকশন render( <Provider store={store}> <UserComponent /> </Provider> ); expect(screen.getByText(/User Name/)).toBeInTheDocument(); });Mocking API Calls: যদি আপনার অ্যাপ্লিকেশন API কল করে, আপনি
jest.mock()ফাংশন ব্যবহার করে API কলগুলো মক করতে পারেন, যাতে টেস্টগুলো দ্রুত এবং নির্ভুলভাবে চালানো যায়।উদাহরণ:
jest.mock('axios');- Testing Middleware: যদি আপনি Redux middleware ব্যবহার করেন (যেমন
redux-thunk,redux-saga), তবে টেস্টিংয়ের সময় মক বা স্পাই ফাংশন ব্যবহার করে Middleware এর কার্যকারিতা যাচাই করা যেতে পারে।
সারাংশ
Unit Testing এবং Integration Testing Redux অ্যাপ্লিকেশন ডেভেলপমেন্টে অত্যন্ত গুরুত্বপূর্ণ। ইউনিট টেস্টিংয়ের মাধ্যমে আপনি ছোট ছোট অংশ যেমন অ্যাকশন, রিডিউসার, এবং ফাংশনগুলো পরীক্ষা করতে পারেন, যেখানে ইন্টিগ্রেশন টেস্টিং আপনাকে স্টোর, অ্যাকশন এবং রিডিউসারের মিথস্ক্রিয়া পরীক্ষা করতে সাহায্য করে। Jest এবং Testing Library Redux অ্যাপ্লিকেশনের টেস্টিংয়ের জন্য সাধারণত ব্যবহৃত টুল। এই টেস্টিং প্রক্রিয়া অ্যাপ্লিকেশনের কোডের নির্ভরযোগ্যতা এবং স্কেলেবিলিটি নিশ্চিত করতে সহায়ক।
Redux অ্যাপ্লিকেশনের actions এবং reducers কে সঠিকভাবে টেস্ট করা অত্যন্ত গুরুত্বপূর্ণ, কারণ তারা স্টেট পরিবর্তনের মূল অংশ। Actions হল অ্যাকশন ক্রিয়েটর যা অ্যাকশন তৈরি করে, এবং Reducers হল সেই ফাংশন যা অ্যাকশন গ্রহণ করে এবং স্টেট পরিবর্তন করে।
Redux অ্যাকশনের এবং রিডিউসারের টেস্টিং সাধারণত Jest এবং Redux Mock Store এর মত টেস্টিং টুলস ব্যবহার করে করা হয়। নিচে আমরা বিস্তারিত আলোচনা করবো কিভাবে actions এবং reducers এর জন্য টেস্ট কেস তৈরি করা যায়।
Jest ব্যবহার করে Actions টেস্ট করা
Redux অ্যাকশনের টেস্টিং শুরু করতে, প্রথমে আপনাকে Jest ইনস্টল করতে হবে (যদি আপনার প্রজেক্টে এটি ইতিমধ্যেই ইনস্টল না থাকে)।
npm install --save-dev jest
Redux অ্যশন সাধারণত অ্যাকশন ক্রিয়েটর ফাংশনের মাধ্যমে তৈরি হয়। এটি একটি প্লেইন ফাংশন যা একটি type এবং প্রাসঙ্গিক payload রিটার্ন করে।
Example Action:
// actions.js
export const increment = (amount) => ({
type: 'INCREMENT',
payload: amount
});
export const decrement = (amount) => ({
type: 'DECREMENT',
payload: amount
});
এখন এই অ্যাকশনের জন্য টেস্ট কেস তৈরি করা যাক।
Action Test Case (Jest):
// actions.test.js
import { increment, decrement } from './actions';
describe('Action Creators', () => {
test('increment action creator returns correct action', () => {
const action = increment(5);
expect(action).toEqual({
type: 'INCREMENT',
payload: 5
});
});
test('decrement action creator returns correct action', () => {
const action = decrement(3);
expect(action).toEqual({
type: 'DECREMENT',
payload: 3
});
});
});
এখানে:
increment(5)এবংdecrement(3)কল করলে তারা সঠিক অ্যাকশন অবজেক্ট রিটার্ন করছে কিনা, তাexpectদিয়ে যাচাই করা হয়েছে।toEqualmatcher দিয়ে আমরা অ্যাকশন অবজেক্টের টাইপ এবং পে-লোড চেক করছি।
Jest ব্যবহার করে Reducers টেস্ট করা
Redux reducer হলো একটি pure function যা অ্যাকশন এবং বর্তমান স্টেট নিয়ে নতুন স্টেট রিটার্ন করে। Reducer টেস্ট করার সময়, আমরা অ্যাকশনের টাইপ এবং পে-লোডের ভিত্তিতে স্টেট চেক করি।
Example Reducer:
// counterReducer.js
const initialState = {
counter: 0
};
const counterReducer = (state = initialState, action) => {
switch (action.type) {
case 'INCREMENT':
return { counter: state.counter + action.payload };
case 'DECREMENT':
return { counter: state.counter - action.payload };
default:
return state;
}
};
export default counterReducer;
এখন, এই reducer-এর জন্য টেস্ট কেস তৈরি করা যাক।
Reducer Test Case (Jest):
// counterReducer.test.js
import counterReducer from './counterReducer';
describe('Counter Reducer', () => {
const initialState = { counter: 0 };
test('should return the initial state', () => {
expect(counterReducer(undefined, {})).toEqual(initialState);
});
test('should handle INCREMENT action', () => {
const action = { type: 'INCREMENT', payload: 5 };
const expectedState = { counter: 5 };
expect(counterReducer(initialState, action)).toEqual(expectedState);
});
test('should handle DECREMENT action', () => {
const action = { type: 'DECREMENT', payload: 3 };
const expectedState = { counter: -3 };
expect(counterReducer(initialState, action)).toEqual(expectedState);
});
});
এখানে:
- প্রথম টেস্টে,
counterReducerকে কোনো অ্যাকশন না দিয়ে ডিফল্ট স্টেট (initial state) এর সাথে চেক করা হয়েছে। - পরবর্তী দুটি টেস্টে,
INCREMENTএবংDECREMENTঅ্যাকশনটি ব্যবহার করে স্টেট পরিবর্তন চেক করা হয়েছে।
Redux Actions এবং Reducers এর জন্য Mock Store টেস্ট করা
Redux অ্যাকশন এবং রিডিউসারের টেস্টিংয়ের জন্য redux-mock-store ব্যবহার করা হয়। এটি Redux স্টোরের একটি মক সংস্করণ তৈরি করে, যা অ্যাকশন ডিসপ্যাচ করে এবং স্টোরের আচরণ পরীক্ষা করতে সাহায্য করে।
redux-mock-store ইনস্টল করা
npm install --save-dev redux-mock-store
Mock Store Test Case:
import configureMockStore from 'redux-mock-store';
import thunk from 'redux-thunk'; // If you're using redux-thunk
import { increment, decrement } from './actions';
const middlewares = [thunk];
const mockStore = configureMockStore(middlewares);
describe('Actions with Mock Store', () => {
it('creates an action to increment counter', () => {
const expectedActions = [
{ type: 'INCREMENT', payload: 5 }
];
const store = mockStore({ counter: 0 });
store.dispatch(increment(5));
expect(store.getActions()).toEqual(expectedActions);
});
it('creates an action to decrement counter', () => {
const expectedActions = [
{ type: 'DECREMENT', payload: 3 }
];
const store = mockStore({ counter: 5 });
store.dispatch(decrement(3));
expect(store.getActions()).toEqual(expectedActions);
});
});
এখানে:
- mockStore দিয়ে একটি মক স্টোর তৈরি করা হয়েছে এবং
incrementওdecrementঅ্যাকশন ডিসপ্যাচ করে, তার রিটার্ন অ্যাকশনটি চেক করা হয়েছে।
সারাংশ
Redux অ্যাকশন এবং রিডিউসারের টেস্টিং করা ডেভেলপারদের জন্য গুরুত্বপূর্ণ, যাতে অ্যাপ্লিকেশনটি প্রত্যাশিতভাবে কাজ করে কিনা তা নিশ্চিত করা যায়। Jest টেস্টিং ফ্রেমওয়ার্ক এবং redux-mock-store ব্যবহার করে, আপনি Redux অ্যাকশন এবং রিডিউসার গুলোর জন্য সহজেই টেস্ট কেস তৈরি করতে পারেন।
- Actions টেস্ট করার সময়, অ্যাকশন ক্রিয়েটর সঠিকভাবে অ্যাকশন রিটার্ন করছে কিনা তা যাচাই করতে হবে।
- Reducers টেস্ট করার সময়, অ্যাকশন ডিসপ্যাচ করার পর স্টেট সঠিকভাবে পরিবর্তিত হচ্ছে কিনা তা যাচাই করতে হবে।
- Mock Store ব্যবহার করে অ্যাকশন ডিসপ্যাচ এবং স্টোরের পরিবর্তন পরীক্ষা করা সম্ভব।
এইভাবে, আপনি নিশ্চিত হতে পারেন যে আপনার Redux স্টেট ম্যানেজমেন্ট সঠিকভাবে কাজ করছে।
Redux অ্যাপ্লিকেশনে middleware এবং Thunk গুরুত্বপূর্ণ ভূমিকা পালন করে। Middleware হল একটি ফাংশন যা Redux স্টোরে অ্যাকশন পাস করার আগে বা পরে অতিরিক্ত কাজ সম্পন্ন করতে সহায়তা করে। এর মাধ্যমে আপনি asynchronous actions, logging, error handling, এবং আরও অনেক ফিচার ইমপ্লিমেন্ট করতে পারেন। Redux Thunk একটি middleware যা asynchronous actions পরিচালনা করতে ব্যবহৃত হয়, যেমন API কল করা এবং তারপর Redux স্টেটে ডেটা আপডেট করা।
যেহেতু middleware এবং Thunk অ্যাসিনক্রোনাস কাজ করে, তাই এগুলোর টেস্টিং অন্যান্য সিনক্রোনাস কোডের তুলনায় কিছুটা আলাদা হতে পারে। এখানে আমরা Redux middleware এবং Thunk এর টেস্টিং নিয়ে আলোচনা করবো।
Redux Middleware এর Testing
Redux middleware যেমন Thunk, Logger ইত্যাদি, মূলত অ্যাকশন ডিসপ্যাচ করার আগে বা পরে স্টোরের মধ্যে পরিবর্তন আনতে ব্যবহৃত হয়। এসব middleware-এর টেস্টিং করতে সাধারণত Jest এবং Redux Mock Store ব্যবহার করা হয়। Redux Mock Store হল একটি টুল যা আপনাকে Redux স্টোরের আচরণ মক করার সুযোগ দেয়।
Redux Middleware Testing উদাহরণ
ধরা যাক, আমাদের একটি Logger middleware আছে যা প্রতিটি অ্যাকশনের নাম কনসোল লগে দেখায়।
// loggerMiddleware.js
const loggerMiddleware = store => next => action => {
console.log('dispatching', action);
return next(action);
};
export default loggerMiddleware;
এখন এই middleware টেস্ট করতে চাইলে আমরা Jest এবং redux-mock-store ব্যবহার করতে পারি।
npm install redux-mock-store --save-dev
টেস্টের জন্য:
// loggerMiddleware.test.js
import configureMockStore from 'redux-mock-store';
import loggerMiddleware from './loggerMiddleware';
const mockStore = configureMockStore([loggerMiddleware]);
describe('loggerMiddleware', () => {
it('should log dispatched actions', () => {
const store = mockStore({});
const action = { type: 'TEST_ACTION' };
// Mock console.log to track the output
const logSpy = jest.spyOn(console, 'log').mockImplementation(() => {});
store.dispatch(action);
// Check if loggerMiddleware logs the action
expect(logSpy).toHaveBeenCalledWith('dispatching', action);
// Restore the spy
logSpy.mockRestore();
});
});
এখানে:
- redux-mock-store ব্যবহার করে আমরা Redux স্টোর মক করেছি।
jest.spyOn(console, 'log')দিয়ে কনসোল লগ ট্র্যাক করেছি এবং পরে চেক করেছি যেdispatchingঅ্যাকশনটি লগ হয়েছে কি না।
Redux Thunk এর Testing
Redux Thunk হল একটি middleware যা asynchronous actions পরিচালনা করতে সাহায্য করে। এর মাধ্যমে আপনি API কল করে ডেটা লোড করার মতো কাজ করতে পারেন। Thunk এর টেস্টিংয়ের জন্য, সাধারণত redux-mock-store এর সাথে jest ব্যবহার করা হয়।
Redux Thunk Testing উদাহরণ
ধরা যাক, আমাদের একটি API কল থাঙ্ক অ্যাকশন আছে যা ডেটা লোড করে এবং তারপর Redux স্টেটে সেই ডেটা আপডেট করে।
// actions.js
export const fetchData = () => {
return (dispatch) => {
dispatch({ type: 'FETCH_DATA_REQUEST' });
fetch('https://jsonplaceholder.typicode.com/posts')
.then(response => response.json())
.then(data => {
dispatch({
type: 'FETCH_DATA_SUCCESS',
payload: data
});
})
.catch(error => {
dispatch({
type: 'FETCH_DATA_FAILURE',
payload: error
});
});
};
};
এখন, এই অ্যাকশন টেস্ট করতে হবে।
npm install redux-mock-store jest-fetch-mock --save-dev
- redux-mock-store: Redux স্টোর মক করার জন্য।
- jest-fetch-mock: API কল মক করার জন্য।
টেস্ট কোড:
// actions.test.js
import configureMockStore from 'redux-mock-store';
import thunk from 'redux-thunk';
import { fetchData } from './actions';
const middlewares = [thunk];
const mockStore = configureMockStore(middlewares);
describe('fetchData action', () => {
it('creates FETCH_DATA_SUCCESS when fetching data has been done', async () => {
// Mock the fetch API response
global.fetch = jest.fn(() =>
Promise.resolve({
json: () => Promise.resolve([{ id: 1, title: 'Test Post' }]),
})
);
const expectedActions = [
{ type: 'FETCH_DATA_REQUEST' },
{
type: 'FETCH_DATA_SUCCESS',
payload: [{ id: 1, title: 'Test Post' }],
},
];
const store = mockStore({});
// Dispatch fetchData action
await store.dispatch(fetchData());
// Check if expected actions were dispatched
expect(store.getActions()).toEqual(expectedActions);
});
it('creates FETCH_DATA_FAILURE when fetching data fails', async () => {
// Mock the fetch API to return an error
global.fetch = jest.fn(() =>
Promise.reject('API is down')
);
const expectedActions = [
{ type: 'FETCH_DATA_REQUEST' },
{
type: 'FETCH_DATA_FAILURE',
payload: 'API is down',
},
];
const store = mockStore({});
// Dispatch fetchData action
await store.dispatch(fetchData());
// Check if expected actions were dispatched
expect(store.getActions()).toEqual(expectedActions);
});
});
এখানে:
- jest.fn() এর মাধ্যমে
fetchফাংশনটি মক করা হয়েছে। - mockStore() ব্যবহার করে Redux স্টোর তৈরি করা হয়েছে।
- store.dispatch(fetchData()) এর মাধ্যমে Thunk অ্যাকশন ডিসপ্যাচ করা হয়েছে এবং পরে অ্যাকশনগুলো যাচাই করা হয়েছে।
Redux Middleware এবং Thunk এর Testing-এর মূল বিষয়সমূহ
- Mock Store:
redux-mock-storeব্যবহার করে স্টোর মক করা হয়, যাতে কোনো রিয়েল স্টোরের প্রয়োজন না হয়। - Async Testing: Thunk অ্যাকশনগুলোর জন্য অ্যাসিনক্রোনাস টেস্টিং করতে
async/awaitব্যবহার করা হয় এবং API কল মক করতেjest-fetch-mockব্যবহার করা হয়। - Jest Spies: Middleware এর টেস্টে কনসোল লগ বা অন্যান্য সাইড ইফেক্ট ট্র্যাক করতে
jest.spyOn()ব্যবহার করা হয়।
সারাংশ
Redux Middleware এবং Thunk এর টেস্টিং সাধারণত redux-mock-store, Jest, এবং jest-fetch-mock ব্যবহার করে করা হয়। Middleware এর টেস্টে সাধারণত অ্যাকশন ডিসপ্যাচ করার আগে বা পরে তার কার্যক্রম যাচাই করা হয়, যেমন Logger middleware এর ক্ষেত্রে অ্যাকশনটি লগ হচ্ছে কি না তা পরীক্ষা করা হয়। Thunk অ্যাকশনের টেস্টে, অ্যাসিনক্রোনাস API কল বা অন্য যে কোনো asynchronous কাজের ফলাফল যাচাই করা হয়, এবং এটি নিশ্চিত করা হয় যে Redux স্টোরে সঠিক অ্যাকশন পাঠানো হচ্ছে।
Redux অ্যাপ্লিকেশন পরীক্ষা (testing) করা একটি গুরুত্বপূর্ণ প্রক্রিয়া, যা আপনার স্টোর, রিডিউসার, একশন, এবং ইউআই ইন্টারঅ্যাকশন ঠিকভাবে কাজ করছে কিনা তা নিশ্চিত করতে সাহায্য করে। React অ্যাপ্লিকেশনগুলিতে Redux স্টোরের সাথে ইন্টিগ্রেশন টেস্টিং করার জন্য Testing Library এবং Jest ব্যবহার করা অত্যন্ত উপকারী। এই টিউটোরিয়ালে আমরা দেখবো কিভাবে Jest এবং Testing Library ব্যবহার করে Redux অ্যাপ্লিকেশন টেস্ট করা যায়।
Jest এবং Testing Library: সংক্ষিপ্ত পরিচিতি
- Jest: এটি একটি JavaScript টেস্টিং ফ্রেমওয়ার্ক যা বিশেষভাবে React অ্যাপ্লিকেশনের জন্য তৈরি করা হয়েছে। Jest ব্যবহার করে আপনি একক (unit), ইন্টিগ্রেশন, এবং স্ন্যাপশট টেস্টিং করতে পারেন।
- React Testing Library: এটি একটি টুল যা React কম্পোনেন্ট টেস্ট করার জন্য ব্যবহৃত হয়। React Testing Library DOM এর সঙ্গে ইন্টারঅ্যাক্ট করে এবং কম্পোনেন্টের আচরণ পরীক্ষা করে।
Redux অ্যাপ্লিকেশন টেস্ট করার ধাপসমূহ
Redux অ্যাপ্লিকেশন টেস্ট করার সময়, আপনি সাধারণত স্টোরের কার্যকারিতা, একশন ডিসপ্যাচিং, রিডিউসার ফাংশন এবং ইউআই (UI) ইন্টারঅ্যাকশন পরীক্ষা করবেন। এখানে Redux অ্যাপ্লিকেশন টেস্ট করার জন্য প্রয়োজনীয় ধাপগুলো আলোচনা করা হলো।
১. Redux Store টেস্ট করা
Redux স্টোরের টেস্টিং মূলত স্টেট সঠিকভাবে আপডেট হচ্ছে কিনা, এবং একশন ডিসপ্যাচ করার পর সঠিক রিডিউসার কাজ করছে কিনা তা নিশ্চিত করা।
উদাহরণ: স্লাইস এবং রিডিউসার টেস্ট
ধরা যাক, আপনার একটি Todo অ্যাপ্লিকেশন আছে, যেখানে addTodo এবং removeTodo একশন রয়েছে।
স্লাইস (todoSlice.js):
import { createSlice } from '@reduxjs/toolkit';
const todoSlice = createSlice({
name: 'todos',
initialState: [],
reducers: {
addTodo: (state, action) => {
state.push(action.payload);
},
removeTodo: (state, action) => {
return state.filter(todo => todo.id !== action.payload.id);
},
},
});
export const { addTodo, removeTodo } = todoSlice.actions;
export default todoSlice.reducer;
স্টোর (store.js):
import { configureStore } from '@reduxjs/toolkit';
import todoReducer from './todoSlice';
const store = configureStore({
reducer: {
todos: todoReducer,
},
});
export default store;
স্লাইস টেস্ট করা:
Jest ব্যবহার করে স্লাইস এবং একশনগুলি টেস্ট করা হবে।
টেস্ট ফাইল (todoSlice.test.js):
import todoReducer, { addTodo, removeTodo } from './todoSlice';
describe('todoSlice', () => {
let initialState;
beforeEach(() => {
initialState = [];
});
test('should add a todo', () => {
const todo = { id: 1, text: 'Learn Redux' };
const action = addTodo(todo);
const state = todoReducer(initialState, action);
expect(state).toHaveLength(1);
expect(state[0]).toEqual(todo);
});
test('should remove a todo', () => {
const todo = { id: 1, text: 'Learn Redux' };
initialState.push(todo);
const action = removeTodo({ id: 1 });
const state = todoReducer(initialState, action);
expect(state).toHaveLength(0);
});
});
এই টেস্টগুলো নিশ্চিত করে যে:
addTodoএকশন স্টেটে নতুন টুডো যোগ করছে।removeTodoএকশন স্টেট থেকে টুডো মুছে ফেলছে।
২. React কম্পোনেন্ট টেস্ট করা
Redux স্টোরের সাথে যুক্ত React কম্পোনেন্টগুলোও টেস্ট করা প্রয়োজন। React Testing Library ব্যবহার করে Redux স্টোর এবং কম্পোনেন্টের ইন্টিগ্রেশন টেস্ট করা যায়।
উদাহরণ: TodoApp কম্পোনেন্ট টেস্ট করা
React কম্পোনেন্ট (TodoApp.js):
import React from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { addTodo, removeTodo } from './todoSlice';
const TodoApp = () => {
const dispatch = useDispatch();
const todos = useSelector((state) => state.todos);
const handleAddTodo = () => {
const todo = { id: Date.now(), text: 'Learn Redux' };
dispatch(addTodo(todo));
};
const handleRemoveTodo = (id) => {
dispatch(removeTodo({ id }));
};
return (
<div>
<button onClick={handleAddTodo}>Add Todo</button>
<ul>
{todos.map((todo) => (
<li key={todo.id}>
{todo.text} <button onClick={() => handleRemoveTodo(todo.id)}>Delete</button>
</li>
))}
</ul>
</div>
);
};
export default TodoApp;
কম্পোনেন্ট টেস্ট করা:
React Testing Library এবং Jest ব্যবহার করে TodoApp কম্পোনেন্টের টেস্ট করা হবে। এখানে আমরা Redux স্টোরের সাথে React কম্পোনেন্ট টেস্ট করব।
টেস্ট ফাইল (TodoApp.test.js):
import { render, screen, fireEvent } from '@testing-library/react';
import { Provider } from 'react-redux';
import store from './store';
import TodoApp from './TodoApp';
describe('TodoApp', () => {
test('should add a todo item', () => {
render(
<Provider store={store}>
<TodoApp />
</Provider>
);
const addButton = screen.getByText('Add Todo');
fireEvent.click(addButton);
const todoItem = screen.getByText('Learn Redux');
expect(todoItem).toBeInTheDocument();
});
test('should remove a todo item', () => {
render(
<Provider store={store}>
<TodoApp />
</Provider>
);
const addButton = screen.getByText('Add Todo');
fireEvent.click(addButton);
const deleteButton = screen.getByText('Delete');
fireEvent.click(deleteButton);
const todoItem = screen.queryByText('Learn Redux');
expect(todoItem).toBeNull();
});
});
এখানে:
render(): কম্পোনেন্টটি DOM এ রেন্ডার করে।screen.getByText(): একটি DOM উপাদান (যেমন, টেক্সট) অনুসন্ধান করে।fireEvent.click(): একটি ইউজার ইভেন্ট সিমুলেট করে (যেমন বাটনে ক্লিক করা)।expect(): টেস্ট assertion ব্যবহার করে নিশ্চিত করা হয় যে টেস্ট সফল হয়েছে।
এই টেস্টটি নিশ্চিত করে যে:
- Add Todo বাটনে ক্লিক করলে নতুন টুডো যোগ হচ্ছে।
- Delete বাটনে ক্লিক করলে টুডো মুছে ফেলা হচ্ছে।
৩. Mocking Dispatch in Tests
কিছু ক্ষেত্রে, আপনাকে dispatch ফাংশনটি মক (mock) করতে হতে পারে, বিশেষত যখন আপনি একশন বা রিডিউসার ফাংশন টেস্ট করতে চান।
মকিং Dispatch:
import { render, screen, fireEvent } from '@testing-library/react';
import { Provider } from 'react-redux';
import { useDispatch } from 'react-redux';
import TodoApp from './TodoApp';
jest.mock('react-redux', () => ({
...jest.requireActual('react-redux'),
useDispatch: jest.fn(),
}));
describe('TodoApp', () => {
test('should dispatch addTodo action when button is clicked', () => {
const mockDispatch = jest.fn();
useDispatch.mockReturnValue(mockDispatch);
render(
<Provider store={store}>
<TodoApp />
</Provider>
);
const addButton = screen.getByText('Add Todo');
fireEvent.click(addButton);
expect(mockDispatch).toHaveBeenCalledWith(expect.objectContaining({ type: 'ADD_TODO' }));
});
});
এখানে, useDispatch হুককে মক করা হয়েছে, যাতে dispatch ফাংশনটি মক করা হয় এবং আপনি টেস্টে একশন ডিসপ্যাচ চেক করতে পারেন।
সারাংশ
Redux অ্যাপ্লিকেশন টেস্টিং অত্যন্ত গুরুত্বপূর্ণ এবং এটি নিশ্চিত করে যে আপনার অ্যাপ্লিকেশন সঠিকভাবে কাজ করছে। Jest এবং React Testing Library ব্যবহার করে আপনি Redux স্টোর, রিডিউসার, একশন, এবং কম্পোনেন্টের কার্যকারিতা পরীক্ষা করতে পারেন। টেস্টিং এর মাধ্যমে আপনি আপনার কোডের স্থায়িত্ব এবং কার্যকারিতা নিশ্চিত করতে পারেন, যা দীর্ঘমেয়াদে ডিবাগিং এবং উন্নয়নে সাহায্য করে।
Read more