বাংলা

জাভাস্ক্রিপ্টে টেস্ট-ড্রিভেন ডেভেলপমেন্ট (TDD) আয়ত্ত করুন। এই সম্পূর্ণ নির্দেশিকাটি রেড-গ্রিন-রিফ্যাক্টর চক্র, Jest-এর সাথে বাস্তবায়ন, এবং আধুনিক ডেভেলপমেন্টের সেরা অনুশীলনগুলি তুলে ধরে।

জাভাস্ক্রিপ্টে টেস্ট-ড্রিভেন ডেভেলপমেন্ট: বিশ্বব্যাপী ডেভেলপারদের জন্য একটি সম্পূর্ণ নির্দেশিকা

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

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

টেস্ট-ড্রিভেন ডেভেলপমেন্ট (TDD) কী?

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

TDD-এর ছন্দ: রেড-গ্রিন-রিফ্যাক্টর

এই তিন-ধাপের চক্রটি হলো TDD-এর হৃদস্পন্দন। এই ছন্দ বোঝা এবং অনুশীলন করা এই কৌশলটি আয়ত্ত করার জন্য মৌলিক।

একবার একটি ছোট কার্যকারিতার জন্য চক্রটি সম্পূর্ণ হলে, আপনি পরবর্তী অংশের জন্য একটি নতুন ব্যর্থ টেস্ট দিয়ে আবার শুরু করেন।

TDD-এর তিনটি নিয়ম

রবার্ট সি. মার্টিন (যিনি "আঙ্কেল বব" নামে পরিচিত), অ্যাজাইল সফটওয়্যার আন্দোলনের একজন গুরুত্বপূর্ণ ব্যক্তিত্ব, তিনটি সহজ নিয়ম সংজ্ঞায়িত করেছেন যা TDD শৃঙ্খলাকে বিধিবদ্ধ করে:

  1. একটি ব্যর্থ ইউনিট টেস্ট পাস করানোর প্রয়োজন না হলে আপনি কোনো প্রোডাকশন কোড লিখবেন না।
  2. একটি ইউনিট টেস্ট ব্যর্থ করার জন্য যতটুকু প্রয়োজন, তার চেয়ে বেশি লিখবেন না; এবং কম্পাইলেশন ব্যর্থতাও একটি ব্যর্থতা।
  3. একটি ব্যর্থ ইউনিট টেস্ট পাস করার জন্য যতটুকু প্রয়োজন, তার চেয়ে বেশি প্রোডাকশন কোড লিখবেন না।

এই নিয়মগুলি অনুসরণ করা আপনাকে রেড-গ্রিন-রিফ্যাক্টর চক্রে বাধ্য করে এবং নিশ্চিত করে যে আপনার ১০০% প্রোডাকশন কোড একটি নির্দিষ্ট, পরীক্ষিত প্রয়োজনীয়তা পূরণের জন্য লেখা হয়েছে।

আপনার কেন TDD গ্রহণ করা উচিত? বিশ্বব্যাপী ব্যবসায়িক প্রেক্ষাপট

যদিও TDD স্বতন্ত্র ডেভেলপারদের জন্য বিশাল উপকারিতা প্রদান করে, এর আসল শক্তি দল এবং ব্যবসায়িক স্তরে উপলব্ধি করা যায়, বিশেষ করে বিশ্বব্যাপী বিস্তৃত পরিবেশে।

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

জাভাস্ক্রিপ্টে TDD শুরু করার জন্য আপনার কয়েকটি টুলের প্রয়োজন। আধুনিক জাভাস্ক্রিপ্ট ইকোসিস্টেম চমৎকার বিকল্প সরবরাহ করে।

একটি টেস্টিং স্ট্যাকের মূল উপাদান

এর সরলতা এবং অল-ইন-ওয়ান প্রকৃতির জন্য, আমরা আমাদের উদাহরণগুলির জন্য Jest ব্যবহার করব। এটি এমন দলগুলির জন্য একটি চমৎকার পছন্দ যারা একটি "জিরো-কনফিগারেশন" অভিজ্ঞতা খুঁজছেন।

Jest দিয়ে ধাপে ধাপে সেটআপ

চলুন TDD-এর জন্য একটি নতুন প্রজেক্ট সেট আপ করি।

১. আপনার প্রজেক্ট শুরু করুন: আপনার টার্মিনাল খুলুন এবং একটি নতুন প্রজেক্ট ডিরেক্টরি তৈরি করুন।

mkdir js-tdd-project
cd js-tdd-project
npm init -y

২. Jest ইনস্টল করুন: আপনার প্রজেক্টে Jest-কে একটি ডেভেলপমেন্ট ডিপেন্ডেন্সি হিসাবে যোগ করুন।

npm install --save-dev jest

৩. টেস্ট স্ক্রিপ্ট কনফিগার করুন: আপনার `package.json` ফাইলটি খুলুন। `"scripts"` বিভাগটি খুঁজুন এবং `"test"` স্ক্রিপ্টটি পরিবর্তন করুন। একটি `"test:watch"` স্ক্রিপ্ট যোগ করারও খুব সুপারিশ করা হয়, যা TDD ওয়ার্কফ্লোর জন্য অমূল্য।

"scripts": {
  "test": "jest",
  "test:watch": "jest --watchAll"
}

`--watchAll` ফ্ল্যাগটি Jest-কে বলে যে কোনো ফাইল সেভ হলেই স্বয়ংক্রিয়ভাবে টেস্ট পুনরায় চালাতে। এটি তাৎক্ষণিক ফিডব্যাক প্রদান করে, যা রেড-গ্রিন-রিফ্যাক্টর চক্রের জন্য উপযুক্ত।

এটাই! আপনার এনভায়রনমেন্ট প্রস্তুত। Jest স্বয়ংক্রিয়ভাবে `*.test.js`, `*.spec.js` নামের বা `__tests__` ডিরেক্টরিতে অবস্থিত টেস্ট ফাইলগুলি খুঁজে নেবে।

অনুশীলনে TDD: একটি `CurrencyConverter` মডিউল তৈরি করা

আসুন TDD চক্রটি একটি ব্যবহারিক, বিশ্বব্যাপী বোধগম্য সমস্যায় প্রয়োগ করি: মুদ্রাগুলির মধ্যে অর্থ রূপান্তর। আমরা ধাপে ধাপে একটি `CurrencyConverter` মডিউল তৈরি করব।

প্রথম পুনরাবৃত্তি: সহজ, নির্দিষ্ট হারের রূপান্তর

🔴 রেড: প্রথম ব্যর্থ টেস্টটি লিখুন

আমাদের প্রথম প্রয়োজন হলো একটি নির্দিষ্ট পরিমাণ অর্থকে একটি নির্দিষ্ট হারে এক মুদ্রা থেকে অন্য মুদ্রায় রূপান্তর করা। `CurrencyConverter.test.js` নামে একটি নতুন ফাইল তৈরি করুন।

// CurrencyConverter.test.js
const CurrencyConverter = require('./CurrencyConverter');

describe('CurrencyConverter', () => {
  it('should convert an amount from USD to EUR correctly', () => {
    // Arrange
    const amount = 10; // 10 USD
    const expected = 9.2; // ধরা যাক, নির্দিষ্ট হার 1 USD = 0.92 EUR

    // Act
    const result = CurrencyConverter.convert(amount, 'USD', 'EUR');

    // Assert
    expect(result).toBe(expected);
  });
});

এখন, আপনার টার্মিনাল থেকে টেস্ট ওয়াচারটি চালান:

npm run test:watch

টেস্টটি দর্শনীয়ভাবে ব্যর্থ হবে। Jest `TypeError: Cannot read properties of undefined (reading 'convert')` এর মতো কিছু রিপোর্ট করবে। এটি আমাদের রেড অবস্থা। টেস্টটি ব্যর্থ হয়েছে কারণ `CurrencyConverter`-এর অস্তিত্ব নেই।

🟢 গ্রিন: পাস করার জন্য সবচেয়ে সহজ কোড লিখুন

এখন, চলুন টেস্টটি পাস করাই। `CurrencyConverter.js` তৈরি করুন।

// CurrencyConverter.js
const rates = {
  USD: {
    EUR: 0.92
  }
};

const CurrencyConverter = {
  convert(amount, from, to) {
    return amount * rates[from][to];
  }
};

module.exports = CurrencyConverter;

আপনি এই ফাইলটি সেভ করার সাথে সাথেই, Jest টেস্টটি পুনরায় চালাবে এবং এটি গ্রিন হয়ে যাবে। আমরা টেস্টের প্রয়োজনীয়তা পূরণের জন্য একেবারে ন্যূনতম কোড লিখেছি।

🔵 রিফ্যাক্টর: কোড উন্নত করুন

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

দ্বিতীয় পুনরাবৃত্তি: অজানা মুদ্রা পরিচালনা

🔴 রেড: একটি অবৈধ মুদ্রার জন্য একটি টেস্ট লিখুন

যদি আমরা এমন কোনো মুদ্রায় রূপান্তর করার চেষ্টা করি যা আমরা জানি না, তাহলে কী হওয়া উচিত? সম্ভবত এটি একটি এরর (error) থ্রো করবে। চলুন `CurrencyConverter.test.js`-এ একটি নতুন টেস্টে এই আচরণটি সংজ্ঞায়িত করি।

// CurrencyConverter.test.js-এ, describe ব্লকের ভিতরে

it('should throw an error for unknown currencies', () => {
  // Arrange
  const amount = 10;

  // Act & Assert
  // Jest-এর toThrow কাজ করার জন্য আমরা ফাংশন কলটিকে একটি অ্যারো ফাংশনে র‍্যাপ করি।
  expect(() => {
    CurrencyConverter.convert(amount, 'USD', 'XYZ');
  }).toThrow('Unknown currency: XYZ');
});

ফাইলটি সেভ করুন। টেস্ট রানার অবিলম্বে একটি নতুন ব্যর্থতা দেখাবে। এটি রেড কারণ আমাদের কোড কোনো এরর থ্রো করে না; এটি `rates['USD']['XYZ']` অ্যাক্সেস করার চেষ্টা করে, যার ফলে একটি `TypeError` হয়। আমাদের নতুন টেস্টটি এই ত্রুটিটি সঠিকভাবে শনাক্ত করেছে।

🟢 গ্রিন: নতুন টেস্টটি পাস করান

আসুন ভ্যালিডেশন যোগ করার জন্য `CurrencyConverter.js` পরিবর্তন করি।

// CurrencyConverter.js
const rates = {
  USD: {
    EUR: 0.92,
    GBP: 0.80
  },
  EUR: {
    USD: 1.08
  }
};

const CurrencyConverter = {
  convert(amount, from, to) {
    if (!rates[from] || !rates[from][to]) {
      // একটি ভালো এরর মেসেজের জন্য কোন মুদ্রাটি অজানা তা নির্ধারণ করুন
      const unknownCurrency = !rates[from] ? from : to;
      throw new Error(`Unknown currency: ${unknownCurrency}`);
    }
    return amount * rates[from][to];
  }
};

module.exports = CurrencyConverter;

ফাইলটি সেভ করুন। উভয় টেস্টই এখন পাস করছে। আমরা আবার গ্রিন অবস্থায় ফিরে এসেছি।

🔵 রিফ্যাক্টর: এটি পরিষ্কার করুন

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

তৃতীয় পুনরাবৃত্তি: অ্যাসিঙ্ক্রোনাস রেট ফেচিং

রেট হার্ডকোড করা বাস্তবসম্মত নয়। আসুন আমাদের মডিউলটিকে একটি (মক করা) এক্সটার্নাল API থেকে রেট আনার জন্য রিফ্যাক্টর করি।

🔴 রেড: একটি অ্যাসিঙ্ক টেস্ট লিখুন যা একটি API কল মক করে

প্রথমত, আমাদের কনভার্টারটিকে পুনর্গঠন করতে হবে। এটি এখন একটি ক্লাস হবে যা আমরা সম্ভবত একটি API ক্লায়েন্ট দিয়ে ইনস্ট্যানশিয়েট করতে পারব। আমাদের `fetch` API-কেও মক করতে হবে। Jest এটি সহজ করে তোলে।

আসুন এই নতুন, অ্যাসিঙ্ক্রোনাস বাস্তবতার সাথে খাপ খাইয়ে নিতে আমাদের টেস্ট ফাইলটি পুনরায় লিখি। আমরা আবার হ্যাপি পাথ পরীক্ষা করে শুরু করব।

// CurrencyConverter.test.js
const CurrencyConverter = require('./CurrencyConverter');

// এক্সটার্নাল ডিপেন্ডেন্সি মক করুন
global.fetch = jest.fn();

beforeEach(() => {
  // প্রতিটি টেস্টের আগে মকের ইতিহাস পরিষ্কার করুন
  fetch.mockClear();
});

describe('CurrencyConverter', () => {
  it('should fetch rates and convert correctly', async () => {
    // Arrange
    // সফল API রেসপন্স মক করুন
    fetch.mockResolvedValueOnce({
      json: () => Promise.resolve({ rates: { EUR: 0.92 } })
    });

    const converter = new CurrencyConverter('https://api.exchangerates.com');
    const amount = 10; // 10 USD

    // Act
    const result = await converter.convert(amount, 'USD', 'EUR');

    // Assert
    expect(result).toBe(9.2);
    expect(fetch).toHaveBeenCalledTimes(1);
    expect(fetch).toHaveBeenCalledWith('https://api.exchangerates.com/latest?base=USD');
  });

  // আমরা API ব্যর্থতা ইত্যাদির জন্যেও টেস্ট যোগ করব।
});

এটি চালালে রেড এর বন্যা বয়ে যাবে। আমাদের পুরনো `CurrencyConverter` একটি ক্লাস নয়, এর কোনো `async` মেথড নেই, এবং এটি `fetch` ব্যবহার করে না।

🟢 গ্রিন: অ্যাসিঙ্ক লজিক প্রয়োগ করুন

এখন, আসুন টেস্টের প্রয়োজনীয়তা মেটাতে `CurrencyConverter.js` পুনরায় লিখি।

// CurrencyConverter.js
class CurrencyConverter {
  constructor(apiUrl) {
    this.apiUrl = apiUrl;
  }

  async convert(amount, from, to) {
    const response = await fetch(`${this.apiUrl}/latest?base=${from}`);
    if (!response.ok) {
      throw new Error('Failed to fetch exchange rates.');
    }

    const data = await response.json();
    const rate = data.rates[to];

    if (!rate) {
      throw new Error(`Unknown currency: ${to}`);
    }

    // টেস্টে ফ্লোটিং পয়েন্ট সমস্যা এড়াতে সহজ রাউন্ডিং
    const convertedAmount = amount * rate;
    return parseFloat(convertedAmount.toFixed(2));
  }
}

module.exports = CurrencyConverter;

আপনি যখন সেভ করবেন, টেস্টটি গ্রিন হয়ে যাওয়া উচিত। লক্ষ্য করুন যে আমরা ফ্লোটিং-পয়েন্ট ত্রুটিগুলি পরিচালনা করার জন্য রাউন্ডিং লজিকও যোগ করেছি, যা আর্থিক গণনার একটি সাধারণ সমস্যা।

🔵 রিফ্যাক্টর: অ্যাসিঙ্ক কোড উন্নত করুন

`convert` মেথডটি অনেক কিছু করছে: ফেচিং, এরর হ্যান্ডলিং, পার্সিং এবং গণনা। আমরা শুধুমাত্র API কমিউনিকেশনের জন্য দায়ী একটি পৃথক `RateFetcher` ক্লাস তৈরি করে এটিকে রিফ্যাক্টর করতে পারি। আমাদের `CurrencyConverter` তখন এই ফেচারটি ব্যবহার করবে। এটি সিঙ্গেল রেসপন্সিবিলিটি প্রিন্সিপল (Single Responsibility Principle) অনুসরণ করে এবং উভয় ক্লাসকে পরীক্ষা ও রক্ষণাবেক্ষণ করা সহজ করে তোলে। TDD আমাদের এই পরিচ্ছন্ন ডিজাইনের দিকে পরিচালিত করে।

সাধারণ TDD প্যাটার্ন এবং অ্যান্টি-প্যাটার্ন

আপনি TDD অনুশীলন করার সাথে সাথে, আপনি এমন প্যাটার্ন আবিষ্কার করবেন যা ভাল কাজ করে এবং অ্যান্টি-প্যাটার্ন যা ঘর্ষণ সৃষ্টি করে।

অনুসরণ করার জন্য ভাল প্যাটার্ন

এড়িয়ে চলার জন্য অ্যান্টি-প্যাটার্ন

বৃহত্তর ডেভেলপমেন্ট জীবনচক্রে TDD

TDD শূন্যস্থানে বিদ্যমান নয়। এটি আধুনিক অ্যাজাইল এবং ডেভঅপ্স অনুশীলনের সাথে সুন্দরভাবে সংহত হয়, বিশেষ করে বিশ্বব্যাপী দলগুলির জন্য।

উপসংহার: TDD-এর সাথে আপনার যাত্রা

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

শেখার পথটি খাড়া মনে হতে পারে, এবং প্রাথমিক গতি ধীর মনে হতে পারে। কিন্তু ডিবাগিং সময় হ্রাস, উন্নত সফটওয়্যার ডিজাইন এবং বর্ধিত ডেভেলপার আত্মবিশ্বাসের দীর্ঘমেয়াদী সুফল অপরিমেয়। TDD আয়ত্ত করার যাত্রাটি শৃঙ্খলা এবং অনুশীলনের একটি যাত্রা।

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