कॉम्पोनेंट की टेस्टिंग चल रही है

व्यावहारिक परीक्षण कोड का प्रदर्शन शुरू करने के लिए घटक परीक्षण एक अच्छी जगह है. कॉम्पोनेंट टेस्ट, सामान्य यूनिट टेस्ट के मुकाबले ज़्यादा मुश्किल होते हैं. साथ ही, ये एंड-टू-एंड टेस्टिंग से कम जटिल होते हैं. साथ ही, ये डीओएम के साथ इंटरैक्ट करके दिखाते हैं. सैद्धांतिक तौर पर, React के इस्तेमाल से वेब डेवलपर के लिए यह समझना आसान हो गया है कि वेबसाइटें या वेब ऐप्लिकेशन, कॉम्पोनेंट से बने हैं.

इसलिए, अलग-अलग कॉम्पोनेंट को टेस्ट करना, नए या मौजूदा ऐप्लिकेशन की जांच करने के लिए एक अच्छा तरीका है. इससे कोई फ़र्क़ नहीं पड़ता कि वे कितने जटिल हैं.

इस पेज पर एक छोटे कॉम्पोनेंट की जांच की गई है. इसकी बाहरी डिपेंडेंसी जटिल है. ऐसे कॉम्पोनेंट की जांच आसानी से की जा सकती है जो किसी दूसरे कोड के साथ इंटरैक्ट नहीं करता. उदाहरण के लिए, किसी बटन पर क्लिक करके और संख्या बढ़ने की पुष्टि करके. असल में, ऐसा बहुत कम कोड होता है और जिस टेस्ट कोड में इंटरैक्शन नहीं होता वह सीमित वैल्यू वाला हो सकता है.

(यह पूरे ट्यूटोरियल के तौर पर नहीं दिया गया है. बाद में, ऑटोमेटेड टेस्टिंग सेक्शन में सैंपल कोड की मदद से असली साइट की जांच की जाएगी. इसे ट्यूटोरियल के तौर पर इस्तेमाल किया जा सकता है. हालांकि, इस पेज पर अब भी कॉम्पोनेंट की प्रैक्टिस करने के कई उदाहरण दिए गए हैं.)

जिस कॉम्पोनेंट की जांच की जा रही है

हम प्रतिक्रिया वाले कॉम्पोनेंट की जांच करने के लिए, Vitest और इसके JSDOM एनवायरमेंट का इस्तेमाल करेंगे. इससे हम ब्राउज़र की नकल करते समय कमांड लाइन पर नोड का इस्तेमाल करके तेज़ी से टेस्ट कर सकते हैं.

नामों की सूची, जिसमें हर नाम के आगे
    'चुनें' बटन है.
यह एक छोटा रिऐक्ट कॉम्पोनेंट है, जो नेटवर्क पर मौजूद उपयोगकर्ताओं की सूची दिखाता है.

UserList नाम का यह प्रतिक्रिया कॉम्पोनेंट, नेटवर्क से उपयोगकर्ताओं की सूची फ़ेच करता है और आपको उनमें से किसी एक को चुनने की सुविधा देता है. उपयोगकर्ताओं की सूची पाने के लिए, useEffect के अंदर fetch का इस्तेमाल किया जाता है और चुनने वाले हैंडलर को Context ने पास किया जाता है. यह उसका कोड है:

import React, { useEffect, useState, useContext } from 'react';
import { UserContext } from './UserContext.tsx';
import { UserRow } from './UserRow.tsx';

export function UserList({ count = 4 }: { count?: number }) {
  const [users, setUsers] = useState<any[]>([]);
  useEffect(() => {
    fetch('https://jsonplaceholder.typicode.com/users?_limit=' + count)
      .then((response) => response.json())
      .then((json) => setUsers(json));
  }, [count]);

  const c = useContext(UserContext);
  return (
    <div>
      <h2>Users</h2>
      <ul>
        {users.map((u) => (
          <li key={u.id}>
            <button onClick={() => c.userChosen(u.id)}>Choose</button>{' '}
            <UserRow u={u} />
          </li>
        ))}
      </ul>
    </div>
  );
}

इस उदाहरण में, प्रतिक्रिया देने के सबसे सही तरीकों के बारे में नहीं बताया गया है. उदाहरण के लिए, यह useEffect में fetch का इस्तेमाल करता है. हालांकि, आपके कोड बेस में इस तरह के कई मामले हो सकते हैं. इस बात पर ध्यान दें कि पहली बार में ही इन मामलों को समझना मुश्किल लग सकता है. इस कोर्स के आने वाले सेक्शन में, टेस्ट करने लायक कोड लिखने के बारे में ज़्यादा जानकारी दी जाएगी.

इस उदाहरण में, हम इन चीज़ों की जांच कर रहे हैं:

  • देखें कि नेटवर्क से मिलने वाले डेटा की प्रतिक्रिया में कुछ सही DOM बनाए गए हैं या नहीं.
  • पुष्टि करें कि उपयोगकर्ता पर क्लिक करने से कॉलबैक ट्रिगर होता है.

हर कॉम्पोनेंट अलग होता है. यह टेस्टिंग में क्यों दिलचस्प है?

  • यह नेटवर्क से रीयल-लाइफ़ डेटा का अनुरोध करने के लिए ग्लोबल fetch का इस्तेमाल करता है, जो जांच में कम या ज़्यादा हो सकता है.
  • यह UserRow नाम की एक और क्लास इंपोर्ट करता है, जिसकी जांच हम बिना किसी साफ़ तौर पर नहीं करना चाहेंगे.
  • यह ऐसे Context का इस्तेमाल करता है जो खास तौर पर जांच वाले कोड का हिस्सा नहीं है. साथ ही, यह आम तौर पर कोई पैरंट कॉम्पोनेंट उपलब्ध कराता है.

शुरू करने के लिए क्विक टेस्ट लिखें

हम इस कॉम्पोनेंट के बारे में बुनियादी जानकारी की तुरंत जांच कर सकते हैं. साफ़ तौर पर कहें तो, यह उदाहरण बहुत काम का नहीं है! हालांकि, UserList.test.tsx नाम की पीयर फ़ाइल में बॉयलरप्लेट सेट अप करना फ़ायदेमंद होता है (याद रखें कि Vitest जैसे टेस्ट रनर डिफ़ॉल्ट रूप से उन फ़ाइलों को चलाएंगे जिनके आखिर में .test.js या इससे मिलती-जुलती फ़ाइलें हों, जिनमें .tsx भी शामिल है):

import { vi, test, assert, afterAll } from 'vitest';
import { render } from '@testing-library/react';
import { UserList } from './UserList.tsx';
import React, { ContextType } from 'react';

test('render', async () => {
  const c = render(<UserList />);

  const headingNode = await c.findAllByText(/Users);
  assert.isNotNull(headingNode);
});

इस टेस्ट से पता चलता है कि जब कॉम्पोनेंट रेंडर होता है, तब उसमें "उपयोगकर्ता" टेक्स्ट शामिल होता है. यह कॉम्पोनेंट काम करता है भले ही, इस कॉम्पोनेंट से नेटवर्क पर fetch भेजने का खराब असर पड़ता हो. टेस्ट पूरा होने तक, fetch अब भी चल रहा है. इसमें कोई एंडपॉइंट सेट नहीं है. हम इस बात की पुष्टि नहीं कर सकते कि जांच खत्म होने पर उपयोगकर्ता की कोई जानकारी दिखाई जा रही है या कम से कम टाइम आउट का इंतज़ार किए बिना नहीं.

मॉक fetch()

मॉकिंग एक असली फ़ंक्शन या क्लास को टेस्ट करने के लिए, आपके कंट्रोल वाली किसी चीज़ से बदलने की प्रोसेस है. सामान्य यूनिट टेस्ट को छोड़कर, करीब सभी तरह के टेस्ट में ऐसा करना आम बात है. दावा और अन्य प्रिमिटिव में इसके बारे में ज़्यादा जानकारी दी जाएगी.

टेस्ट के लिए, fetch() का मॉक वर्शन बनाया जा सकता है, ताकि यह तुरंत पूरा हो जाए और आपकी उम्मीद के मुताबिक डेटा दिखे. हालांकि, "असल" या अनजान डेटा नहीं. fetch एक ग्लोबल है, जिसका मतलब है कि हमें इसे अपने कोड में import या require की ज़रूरत नहीं है.

Vitest में, आप vi.fn() से मिले एक खास ऑब्जेक्ट के साथ vi.stubGlobal को कॉल करके ग्लोबल का मॉक आउट कर सकते हैं—इससे एक मॉक बन जाता है जिसे हम बाद में बदल सकते हैं. इन तरीकों के बारे में, इस कोर्स के बाद के सेक्शन में ज़्यादा जानकारी दी जाएगी. हालांकि, इन्हें नीचे दिए गए कोड में देखा जा सकता है:

test('render', async () => {
  const fetchMock = vi.fn();
  fetchMock.mockReturnValue(
    Promise.resolve({
      json: () => Promise.resolve([{ name: 'Sam', id: 'sam' }]),
    }),
  );
  vi.stubGlobal('fetch', fetchMock);

  const c = render(<UserList />);

  const headingNode = await c.queryByText(/Users);
  assert.isNotNull(headingNode);

  await waitFor(async () => {
    const samNode = await c.queryByText(/Sam);
    assert.isNotNull(samNode);
  });
});

afterAll(() => {
  vi.unstubAllGlobals();
});

यह कोड एक मॉक जोड़ता है और नेटवर्क फ़ेच के "फ़ेक" वर्शन के बारे में बताता है Response. इसके बाद, कोड के दिखने का इंतज़ार करता है. अगर टेक्स्ट नहीं दिखता है, तो queryByText में मौजूद क्वेरी को नए नाम में बदलकर यह देखा जा सकता है कि जांच नहीं हो पाएगी.

इस उदाहरण में Vitest के मॉकिंग हेल्पर का इस्तेमाल किया गया है. हालांकि, टेस्टिंग के दूसरे फ़्रेमवर्क में भी मॉक बनाने का तरीका मिलता-जुलता है. Vitest इतना यूनीक है कि आपको सभी टेस्ट के बाद vi.unstubAllGlobals() को कॉल करना होगा या इसके बराबर का कोई ग्लोबल विकल्प सेट करना होगा. हमारे काम को "पहले जैसा" किए बिना, fetch मॉक अन्य टेस्ट पर असर डाल सकता है. हर अनुरोध का जवाब JSON के हमारे अनोखे कलेक्शन के साथ दिया जाएगा!

मॉक इंपोर्ट

आपने देखा होगा कि हमारा UserList कॉम्पोनेंट, UserRow नाम का एक कॉम्पोनेंट इंपोर्ट करता है. हमने इस कोड का कोड शामिल नहीं किया है, लेकिन यह उपयोगकर्ता के नाम को रेंडर करता है: पिछली जांच में, "Sam" की जांच की गई थी और इसे सीधे UserList में रेंडर नहीं किया गया था. इसलिए, यह UserRow से ही आना चाहिए.

हमारे कॉम्पोनेंट में उपयोगकर्ताओं के नाम किस तरह से आगे बढ़ते हैं, इस बारे में बताने वाला फ़्लोचार्ट.
UserListTest के पास UserRow का डेटा नहीं है.

हालांकि, UserRow खुद में एक जटिल कॉम्पोनेंट हो सकता है—यह उपयोगकर्ता का ज़्यादा डेटा हासिल कर सकता है या इसके खराब असर भी हो सकते हैं जो हमारी जांच के लिए काम के न हों. उस बदलाव को हटाने से आपके टेस्ट ज़्यादा मददगार होंगे. खास तौर पर, जब आपको जिन कॉम्पोनेंट को टेस्ट करना है वे ज़्यादा जटिल और अलग-अलग डिपेंडेंसी के साथ जुड़े होंगे.

अच्छी बात यह है कि आप कुछ इंपोर्ट को मॉक आउट करने के लिए Vitest का इस्तेमाल कर सकते हैं, भले ही आपके टेस्ट में उनका सीधे तौर पर इस्तेमाल न किया गया हो. इससे उनका इस्तेमाल करने वाला कोई भी कोड एक आसान या जाना-पहचाना वर्शन के साथ उपलब्ध कराया जा सकता है:

vi.mock('./UserRow.tsx', () => {
  return {
    UserRow(arg) {
      return <>{arg.u.name}</>;
    },
  }
});

test('render', async () => {
  // ...
});

fetch ग्लोबल का मज़ाक़ उड़ाने की तरह, यह एक बेहतरीन टूल है. हालांकि, अगर आपके कोड में कई डिपेंडेंसी हैं, तो यह लंबे समय तक काम नहीं करेगा. एक बार फिर, इसका सबसे अच्छा समाधान यह है कि टेस्ट करने लायक कोड लिखना.

क्लिक करें और संदर्भ उपलब्ध कराएं

रिऐक्ट और जैसे कि Lit जैसी अन्य लाइब्रेरी में Context नाम का सिद्धांत होता है. सैंपल कोड में एक UserContext शामिल होता है. यह तरीका तब बताता है, जब किसी उपयोगकर्ता को चुना जाता है. इसे अक्सर "प्रॉप ड्रिलिंग" के विकल्प के तौर पर देखा जाता है, जहां कॉलबैक सीधे UserList को भेजा जाता है.

हमने जो टेस्ट हार्नेस लिखा है, उसने UserContext उपलब्ध नहीं कराया है. इसके बिना प्रतिक्रिया टेस्ट में क्लिक ऐक्शन जोड़ने से, सबसे खराब स्थिति में टेस्ट क्रैश हो जाएगा या सबसे अच्छी तरह से, अगर कोई डिफ़ॉल्ट इंस्टेंस कहीं और दिया गया था, तो कुछ कार्रवाइयां हमारे कंट्रोल से बाहर हो जाएंगी (जैसा कि ऊपर दिए गए UserRow के बारे में जानकारी नहीं है):

  const c = render(<UserList />);
  const chooseButton = await c.getByText(/Choose);
  chooseButton.click();

इसके बजाय, कॉम्पोनेंट को रेंडर करते समय, खुद का Context दिया जा सकता है. इस उदाहरण में vi.fn() के एक इंस्टेंस का इस्तेमाल किया जाता है, जो कि Vitest Mock फ़ंक्शन है. इसका इस्तेमाल यह पता लगाने के लिए किया जा सकता है कि कॉल किया गया है या नहीं और इसके साथ किन तर्कों का इस्तेमाल किया गया है. हमारे मामले में, यह पिछले उदाहरण में मौजूद मॉक fetch के साथ इंटरैक्ट करता है. साथ ही, टेस्ट इस बात की पुष्टि कर सकता है कि पास किया गया आईडी "sam" था:

  const userChosenFn = vi.fn();
  const ucForTest: ContextType<typeof UserContext> = { userChosen: userChosenFn as any };
  const c = render(
    <UserContext.Provider value={ucForTest}>
      <UserList />
    </UserContext.Provider>,
  );

  const chooseButton = await c.getByText(/Choose);
  chooseButton.click();
  assert.deepStrictEqual(userChosenFn.mock.calls, [['sam']]);

यह एक आसान लेकिन असरदार पैटर्न है. इसकी मदद से, उस मुख्य कॉम्पोनेंट से ग़ैर-ज़रूरी निर्भरता को हटाया जा सकता है जिसकी आपको जांच करनी है.

खास जानकारी

यह एक झटपट और आसान उदाहरण है, जिसमें यह दिखाया गया है कि टेस्ट करने में मुश्किल प्रतिक्रिया कॉम्पोनेंट की जांच करने के लिए कॉम्पोनेंट की जांच कैसे की जाती है. इसमें यह भी बताया गया है कि कॉम्पोनेंट अपनी डिपेंडेंसी (fetch ग्लोबल, इंपोर्ट किया गया सबकॉम्पोनेंट, और Context) के साथ सही तरीके से इंटरैक्ट करता है.

जांचें कि आपको कितना समझ आया

प्रतिक्रिया वाले कॉम्पोनेंट की जांच करने के लिए, कौनसे तरीके इस्तेमाल किए गए थे?

टेस्ट के लिए, आसान कॉन्फ़िगरेशन वाली मुश्किल डिपेंडेंसी को मॉक करना
कॉन्टेक्स्ट का इस्तेमाल करके डिपेंडेंसी इंजेक्शन
स्टबिंग ग्लोबल
जांच की जा रही है कि संख्या बढ़ाई गई है या नहीं