हिन्दी

जावास्क्रिप्ट में टेस्ट-ड्रिवन डेवलपमेंट (TDD) में महारत हासिल करें। यह व्यापक गाइड रेड-ग्रीन-रिफैक्टर चक्र, जेस्ट के साथ व्यावहारिक कार्यान्वयन, और आधुनिक विकास के लिए सर्वोत्तम प्रथाओं को कवर करता है।

जावास्क्रिप्ट में टेस्ट-ड्रिवन डेवलपमेंट: वैश्विक डेवलपर्स के लिए एक व्यापक गाइड

इस परिदृश्य की कल्पना करें: आपको एक बड़े, लिगेसी सिस्टम में कोड के एक महत्वपूर्ण हिस्से को संशोधित करने का काम सौंपा गया है। आप एक भय महसूस करते हैं। क्या आपका बदलाव कुछ और तोड़ देगा? आप कैसे सुनिश्चित हो सकते हैं कि सिस्टम अभी भी इच्छानुसार काम करता है? बदलाव का यह डर सॉफ्टवेयर डेवलपमेंट में एक आम समस्या है, जो अक्सर धीमी प्रगति और नाजुक एप्लीकेशन्स की ओर ले जाता है। लेकिन क्या होगा अगर सॉफ्टवेयर को आत्मविश्वास के साथ बनाने का कोई तरीका हो, एक ऐसा सुरक्षा कवच बनाना जो त्रुटियों को प्रोडक्शन तक पहुँचने से पहले ही पकड़ ले? यही टेस्ट-ड्रिवन डेवलपमेंट (TDD) का वादा है।

TDD केवल एक टेस्टिंग तकनीक नहीं है; यह सॉफ्टवेयर डिजाइन और डेवलपमेंट के लिए एक अनुशासित दृष्टिकोण है। यह पारंपरिक "कोड लिखें, फिर टेस्ट करें" मॉडल को उलट देता है। TDD के साथ, आप प्रोडक्शन कोड लिखने से पहले एक टेस्ट लिखते हैं जो फेल हो जाता है। इस सरल उलटफेर का कोड क्वालिटी, डिज़ाइन और रखरखाव पर गहरा प्रभाव पड़ता है। यह गाइड जावास्क्रिप्ट में TDD को लागू करने पर एक व्यापक, व्यावहारिक नज़र डालेगा, जो पेशेवर डेवलपर्स के वैश्विक दर्शकों के लिए डिज़ाइन किया गया है।

टेस्ट-ड्रिवन डेवलपमेंट (TDD) क्या है?

इसके मूल में, टेस्ट-ड्रिवन डेवलपमेंट एक विकास प्रक्रिया है जो एक बहुत छोटे डेवलपमेंट चक्र की पुनरावृत्ति पर निर्भर करती है। फीचर्स लिखने और फिर उन्हें टेस्ट करने के बजाय, TDD इस बात पर जोर देता है कि टेस्ट पहले लिखा जाए। यह टेस्ट अनिवार्य रूप से फेल हो जाएगा क्योंकि फीचर अभी तक मौजूद नहीं है। डेवलपर का काम तब उस विशिष्ट टेस्ट को पास करने के लिए सबसे सरल संभव कोड लिखना है। एक बार जब यह पास हो जाता है, तो कोड को साफ और बेहतर बनाया जाता है। इस मौलिक लूप को "रेड-ग्रीन-रिफैक्टर" चक्र के रूप में जाना जाता है।

TDD की लय: रेड-ग्रीन-रिफैक्टर

यह तीन-चरणीय चक्र TDD की धड़कन है। इस लय को समझना और अभ्यास करना इस तकनीक में महारत हासिल करने के लिए मौलिक है।

एक बार जब एक छोटी कार्यक्षमता के लिए चक्र पूरा हो जाता है, तो आप अगले हिस्से के लिए एक नए फेल होने वाले टेस्ट के साथ फिर से शुरू करते हैं।

TDD के तीन नियम

रॉबर्ट सी. मार्टिन (जिन्हें अक्सर "अंकल बॉब" के नाम से जाना जाता है), जो एजाइल सॉफ्टवेयर आंदोलन में एक प्रमुख व्यक्ति हैं, ने तीन सरल नियम परिभाषित किए जो TDD अनुशासन को संहिताबद्ध करते हैं:

  1. आप कोई भी प्रोडक्शन कोड तब तक नहीं लिखेंगे जब तक कि वह किसी फेल हो रहे यूनिट टेस्ट को पास कराने के लिए न हो।
  2. आप किसी यूनिट टेस्ट को फेल होने के लिए पर्याप्त से अधिक नहीं लिखेंगे; और कंपाइलेशन विफलताएं भी विफलताएं हैं।
  3. आप उस एक फेल हो रहे यूनिट टेस्ट को पास करने के लिए पर्याप्त से अधिक प्रोडक्शन कोड नहीं लिखेंगे।

इन नियमों का पालन करने से आप रेड-ग्रीन-रिफैक्टर चक्र में मजबूर हो जाते हैं और यह सुनिश्चित होता है कि आपका 100% प्रोडक्शन कोड एक विशिष्ट, टेस्ट की गई आवश्यकता को पूरा करने के लिए लिखा गया है।

आपको TDD क्यों अपनाना चाहिए? वैश्विक व्यापार का मामला

हालांकि TDD व्यक्तिगत डेवलपर्स को भारी लाभ प्रदान करता है, इसकी असली शक्ति टीम और व्यावसायिक स्तर पर महसूस की जाती है, खासकर विश्व स्तर पर वितरित वातावरण में।

अपना जावास्क्रिप्ट TDD वातावरण स्थापित करना

जावास्क्रिप्ट में TDD के साथ आरंभ करने के लिए, आपको कुछ उपकरणों की आवश्यकता है। आधुनिक जावास्क्रिप्ट इकोसिस्टम उत्कृष्ट विकल्प प्रदान करता है।

एक टेस्टिंग स्टैक के मुख्य घटक

इसकी सादगी और ऑल-इन-वन प्रकृति के लिए, हम अपने उदाहरणों के लिए Jest का उपयोग करेंगे। यह "शून्य-कॉन्फ़िगरेशन" अनुभव की तलाश करने वाली टीमों के लिए एक उत्कृष्ट विकल्प है।

Jest के साथ चरण-दर-चरण सेटअप

आइए TDD के लिए एक नया प्रोजेक्ट सेट अप करें।

1. अपने प्रोजेक्ट को इनिशियलाइज़ करें: अपना टर्मिनल खोलें और एक नई प्रोजेक्ट डायरेक्टरी बनाएं।

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

2. Jest इंस्टॉल करें: Jest को अपने प्रोजेक्ट में एक डेवलपमेंट डिपेंडेंसी के रूप में जोड़ें।

npm install --save-dev jest

3. टेस्ट स्क्रिप्ट कॉन्फ़िगर करें: अपनी `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` मॉड्यूल बनाएंगे।

पुनरावृत्ति 1: सरल, निश्चित-दर रूपांतरण

🔴 रेड: पहला फेल होने वाला टेस्ट लिखें

हमारी पहली आवश्यकता एक निश्चित दर का उपयोग करके एक मुद्रा से दूसरी मुद्रा में एक विशिष्ट राशि को परिवर्तित करना है। `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; // Assuming a fixed rate of 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` ऑब्जेक्ट थोड़ा कठोर है। अभी के लिए, यह काफी साफ है। सबसे महत्वपूर्ण बात यह है कि हमारे पास एक टेस्ट द्वारा संरक्षित एक कामकाजी सुविधा है। आइए अगली आवश्यकता पर आगे बढ़ें।

पुनरावृत्ति 2: अज्ञात मुद्राओं को संभालना

🔴 रेड: एक अमान्य मुद्रा के लिए एक टेस्ट लिखें

क्या होना चाहिए यदि हम किसी ऐसी मुद्रा में कनवर्ट करने का प्रयास करते हैं जिसे हम नहीं जानते हैं? इसे शायद एक त्रुटि फेंकनी चाहिए। आइए इस व्यवहार को `CurrencyConverter.test.js` में एक नए टेस्ट में परिभाषित करें।

// In CurrencyConverter.test.js, inside the describe block

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

  // Act & Assert
  // We wrap the function call in an arrow function for Jest's toThrow to work.
  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]) {
      // Determine which currency is unknown for a better error message
      const unknownCurrency = !rates[from] ? from : to;
      throw new Error(`Unknown currency: ${unknownCurrency}`);
    }
    return amount * rates[from][to];
  }
};

module.exports = CurrencyConverter;

फ़ाइल सहेजें। अब दोनों टेस्ट पास हो गए हैं। हम ग्रीन पर वापस आ गए हैं।

🔵 रिफैक्टर: इसे साफ करें

हमारा `convert` फ़ंक्शन बढ़ रहा है। सत्यापन तर्क गणना के साथ मिला हुआ है। हम पठनीयता में सुधार के लिए सत्यापन को एक अलग निजी फ़ंक्शन में निकाल सकते हैं, लेकिन अभी के लिए, यह अभी भी प्रबंधनीय है। मुख्य बात यह है कि हमारे पास इन परिवर्तनों को करने की स्वतंत्रता है क्योंकि हमारे टेस्ट हमें बताएंगे कि क्या हम कुछ तोड़ते हैं।

पुनरावृत्ति 3: एसिंक्रोनस रेट फेचिंग

रेट को हार्डकोड करना यथार्थवादी नहीं है। आइए अपने मॉड्यूल को एक (नकली) बाहरी एपीआई से रेट प्राप्त करने के लिए रिफैक्टर करें।

🔴 रेड: एक एसिंक टेस्ट लिखें जो एपीआई कॉल को मॉक करता है

सबसे पहले, हमें अपने कनवर्टर को पुनर्गठित करने की आवश्यकता है। अब इसे एक क्लास होना होगा जिसे हम इंस्टेंटियेट कर सकते हैं, शायद एक एपीआई क्लाइंट के साथ। हमें `fetch` API को भी मॉक करना होगा। Jest इसे आसान बनाता है।

आइए इस नई, एसिंक्रोनस वास्तविकता को समायोजित करने के लिए अपनी टेस्ट फ़ाइल को फिर से लिखें। हम फिर से हैप्पी पाथ का परीक्षण करके शुरू करेंगे।

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

// Mock the external dependency
global.fetch = jest.fn();

beforeEach(() => {
  // Clear mock history before each test
  fetch.mockClear();
});

describe('CurrencyConverter', () => {
  it('should fetch rates and convert correctly', async () => {
    // Arrange
    // Mock the successful API response
    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');
  });

  // We'd also add tests for API failures, etc.
});

इसे चलाने से रेड का समुद्र बन जाएगा। हमारा पुराना `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}`);
    }

    // Simple rounding to avoid floating point issues in tests
    const convertedAmount = amount * rate;
    return parseFloat(convertedAmount.toFixed(2));
  }
}

module.exports = CurrencyConverter;

जब आप सहेजते हैं, तो टेस्ट ग्रीन हो जाना चाहिए। ध्यान दें कि हमने फ्लोटिंग-पॉइंट अशुद्धियों को संभालने के लिए राउंडिंग लॉजिक भी जोड़ा है, जो वित्तीय गणना में एक आम समस्या है।

🔵 रिफैक्टर: एसिंक कोड में सुधार करें

`convert` मेथड बहुत कुछ कर रहा है: फेचिंग, एरर हैंडलिंग, पार्सिंग और गणना। हम इसे एक अलग `RateFetcher` क्लास बनाकर रिफैक्टर कर सकते हैं जो केवल एपीआई संचार के लिए जिम्मेदार हो। हमारा `CurrencyConverter` फिर इस फेचर का उपयोग करेगा। यह सिंगल रिस्पांसिबिलिटी प्रिंसिपल का पालन करता है और दोनों क्लास को टेस्ट और बनाए रखने में आसान बनाता है। TDD हमें इस स्वच्छ डिजाइन की ओर मार्गदर्शन करता है।

सामान्य TDD पैटर्न और एंटी-पैटर्न

जैसे ही आप TDD का अभ्यास करते हैं, आप उन पैटर्नों की खोज करेंगे जो अच्छी तरह से काम करते हैं और एंटी-पैटर्न जो घर्षण पैदा करते हैं।

अनुसरण करने के लिए अच्छे पैटर्न

बचने के लिए एंटी-पैटर्न

व्यापक विकास जीवनचक्र में TDD

TDD एक निर्वात में मौजूद नहीं है। यह आधुनिक एजाइल और डेवऑप्स प्रथाओं के साथ खूबसूरती से एकीकृत होता है, खासकर वैश्विक टीमों के लिए।

निष्कर्ष: TDD के साथ आपकी यात्रा

टेस्ट-ड्रिवन डेवलपमेंट एक टेस्टिंग रणनीति से कहीं बढ़कर है—यह एक आदर्श बदलाव है कि हम सॉफ्टवेयर डेवलपमेंट से कैसे संपर्क करते हैं। यह गुणवत्ता, आत्मविश्वास और सहयोग की संस्कृति को बढ़ावा देता है। रेड-ग्रीन-रिफैक्टर चक्र एक स्थिर लय प्रदान करता है जो आपको स्वच्छ, मजबूत और रखरखाव योग्य कोड की ओर मार्गदर्शन करता है। परिणामी टेस्ट सूट एक सुरक्षा कवच बन जाता है जो आपकी टीम को रिग्रेशन से बचाता है और जीवित दस्तावेज़ीकरण जो नए सदस्यों को ऑनबोर्ड करता है।

सीखने की अवस्था खड़ी महसूस हो सकती है, और प्रारंभिक गति धीमी लग सकती है। लेकिन डीबगिंग समय में कमी, बेहतर सॉफ्टवेयर डिजाइन और बढ़े हुए डेवलपर आत्मविश्वास में दीर्घकालिक लाभांश अथाह हैं। TDD में महारत हासिल करने की यात्रा अनुशासन और अभ्यास में से एक है।

आज ही शुरू करें। अपने अगले प्रोजेक्ट में एक छोटी, गैर-महत्वपूर्ण सुविधा चुनें और प्रक्रिया के प्रति प्रतिबद्ध हों। पहले टेस्ट लिखें। इसे फेल होते देखें। इसे पास कराएं। और फिर, सबसे महत्वपूर्ण, रिफैक्टर करें। एक ग्रीन टेस्ट सूट से आने वाले आत्मविश्वास का अनुभव करें, और आप जल्द ही आश्चर्य करेंगे कि आपने कभी किसी और तरीके से सॉफ्टवेयर कैसे बनाया।