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

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

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

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

जांचा जा रहा कॉम्पोनेंट

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

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

UserList नाम का यह React कॉम्पोनेंट, नेटवर्क से उपयोगकर्ताओं की सूची फ़ेच करता है और उनमें से किसी एक को चुनने की सुविधा देता है. उपयोगकर्ताओं की सूची, 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>
  );
}

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

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

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

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

  • यह नेटवर्क से असल डेटा का अनुरोध करने के लिए, ग्लोबल 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 चल रहा है और उसका कोई एंडपॉइंट सेट नहीं है. हम इस बात की पुष्टि नहीं कर सकते कि जांच खत्म होने पर, उपयोगकर्ता की कोई जानकारी दिखाई जा रही है या नहीं. हालांकि, टाइम आउट का इंतज़ार किए बिना ऐसा नहीं किया जा सकता.

Mock 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 नाम का एक कॉम्पोनेंट इंपोर्ट करता है. हमने इसका कोड शामिल नहीं किया है, लेकिन आपके पास यह देखने का विकल्प है कि यह उपयोगकर्ता का नाम रेंडर करता है: पिछले टेस्ट में "समीर" की जांच की गई थी और यह सीधे UserList में रेंडर नहीं किया गया था, इसलिए यह UserRow से आना चाहिए.

इस फ़्लोचार्ट में दिखाया गया है कि उपयोगकर्ताओं के नाम हमारे कॉम्पोनेंट में कैसे आते हैं.
UserListTest के पास UserRow को ऐक्सेस करने की अनुमति नहीं है.

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

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

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

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

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

क्लिक करके जानकारी दें

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

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

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

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

हमारे मामले में, यह पहले दिए गए उदाहरण में मौजूद 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.

देखें कि आपको क्या समझ आया

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

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