javascript - जेस्ट: टाइमर और वादा अच्छा काम नहीं करता है।(सेटटाइमआउट और एसक्यूएन फ़ंक्शन)




testing jestjs (2)

एक उपयोग मामला है जो मैं अभी एक समाधान नहीं ढूंढ सका:

function action(){
  return new Promise(function(resolve, reject){
    let poll
    (function run(){
      callAPI().then(function(resp){
        if (resp.completed) {
          resolve(response)
          return
        }
        poll = setTimeout(run, 100)
      })
    })()
  })
}

और परीक्षण जैसा दिखता है:

jest.useFakeTimers()
const promise = action()
// jest.advanceTimersByTime(1000) // this won't work because the timer is not created
await expect(promise).resolves.toEqual(({completed:true})
// jest.advanceTimersByTime(1000) // this won't work either because the promise will never resolve

मूल रूप से कार्रवाई तब तक हल नहीं होगी जब तक कि टाइमर अग्रिम न हो। यहां एक परिपत्र निर्भरता की तरह लगता है: वादा करने के लिए अग्रिम करने के लिए टाइमर की आवश्यकता है, नकली टाइमर अग्रिम करने के लिए हल करने के लिए वादे की आवश्यकता है।

इस कोड पर कोई विचार

jest.useFakeTimers() 

it('simpleTimer', async () => {
  async function simpleTimer(callback) {
    await callback()    // LINE-A without await here, test works as expected.
    setTimeout(() => {
      simpleTimer(callback)
    }, 1000)
  }

  const callback = jest.fn()
  await simpleTimer(callback)
  jest.advanceTimersByTime(8000)
  expect(callback).toHaveBeenCalledTimes(9)
}

`` `

के साथ असफल रहा

Expected mock function to have been called nine times, but it was called two times.

हालाँकि, यदि मैं LINE-A से await हटाता हूं, तो परीक्षण पास हो जाता है।

क्या प्रॉमिस और टाइमर अच्छा काम नहीं करता है?

मुझे लगता है कि कारण शायद जेस्ट हल करने के लिए दूसरे वादे का इंतजार कर रहा है।


हां, आप सही रास्ते पर हैं।

क्या होता है

await simpleTimer(callback) simpleTimer() लौटे हुए प्रॉमिस के लिए इंतजार करेंगे simpleTimer() तो callback() को हल करने के लिए पहली बार बुलाया जाता है और setTimeout() भी कहा जाता है। jest.useFakeTimers() जगह jest.useFakeTimers() को एक मॉक के साथ बदल दिया गया है ताकि मॉक रिकॉर्ड हो कि इसे [ () => { simpleTimer(callback) }, 1000 ] साथ बुलाया गया था।

jest.advanceTimersByTime(8000) रन () => { simpleTimer(callback) } (1000 <8000 के बाद से) जो setTimer(callback) को कॉल करता है, जो दूसरी बार callback() कॉल करता है और await द्वारा बनाए गए वादे को वापस awaitsetTimeout() दूसरी बार नहीं चलता है क्योंकि बाकी setTimer(callback) PromiseJobs कतार में कतारबद्ध है और उसे चलाने का मौका नहीं मिला है।

expect(callback).toHaveBeenCalledTimes(9) यह रिपोर्ट करने में विफल रहता है कि callback() केवल दो बार कॉल किया गया था।

अतिरिक्त जानकारी

यह अच्छा प्रश्न है। यह जावास्क्रिप्ट की कुछ विशिष्ट विशेषताओं पर ध्यान आकर्षित करता है और यह हुड के नीचे कैसे काम करता है।

संदेश कतार

जावास्क्रिप्ट एक संदेश कतार का उपयोग करता है । अगले संदेश को पुनः प्राप्त करने के लिए कतार में रनटाइम रिटर्न से पहले प्रत्येक संदेश को पूरा करने के लिए चलाया जाता है setTimeout() जैसे कार्य कतार में संदेश जोड़ते हैं

नौकरी की कतार

ES6 Job Queues क्युस का परिचय देता है और आवश्यक जॉब PromiseJobs एक PromiseJobs जो "जॉब्स जो एक प्रॉमिस के निपटारे के लिए प्रतिक्रियाएं हैं" को हैंडल करता है। इस कतार में कोई भी कार्य वर्तमान संदेश पूरा होने और अगले संदेश के शुरू होने से पहले चलता है then() PromiseJobs में एक जॉब को PromiseJobs जब प्रॉमिस को PromiseJobs पर बुलाया जाता है।

async / प्रतीक्षा करें

async / await सिर्फ वादों और जनरेटरों पर चीनी की मात्रा है async हमेशा एक वादा लौटाता है और आवश्यक रूप से दिए गए कॉलबैक में बाकी फ़ंक्शन को आवश्यक रूप से लपेटता है, यह वादा किया गया है।

टाइमर मोक्स

jest.useFakeTimers() कहा जाता है जब jest.useFakeTimers() साथ jest.useFakeTimers() जैसे कार्यों को प्रतिस्थापित करके टाइमर मोक्स काम करता है। ये मोज़ेक उन तर्कों को रिकॉर्ड करते हैं, जिनके साथ उन्हें बुलाया गया था। तब जब jest.advanceTimersByTime() को लूप रन कहा जाता है, जो किसी भी कॉलबैक को कॉल करता है, जो बीते हुए समय में शेड्यूल किया गया होता है, जिसमें कॉलबैक चलाते समय कोई भी जोड़ा जाता है।

दूसरे शब्दों में, setTimeout() आम तौर पर संदेशों को कतार में setTimeout() जो तब तक इंतजार करना चाहिए जब तक कि वर्तमान संदेश चलने से पहले पूरा हो जाए। टाइमर मोक्स कॉलबैक को वर्तमान संदेश के भीतर समकालिक रूप से चलाने की अनुमति देता है।

यहाँ एक उदाहरण है जो उपरोक्त जानकारी प्रदर्शित करता है:

jest.useFakeTimers();

test('execution order', async () => {
  const order = [];
  order.push('1');
  setTimeout(() => { order.push('6'); }, 0);
  const promise = new Promise(resolve => {
    order.push('2');
    resolve();
  }).then(() => {
    order.push('4');
  });
  order.push('3');
  await promise;
  order.push('5');
  jest.advanceTimersByTime(0);
  expect(order).toEqual([ '1', '2', '3', '4', '5', '6' ]);
});

अच्छा खेलने के लिए टाइमर मोक्स और वादे कैसे प्राप्त करें

टाइमर मोक्स कॉलबैक को समकालिक रूप से निष्पादित करेगा, लेकिन उन कॉलबैक से PromiseJobs में नौकरियों की कतार PromiseJobs

सौभाग्य से यह वास्तव में काफी आसान है कि PromiseJobs में सभी लंबित नौकरियों को एक PromiseJobs टेस्ट के भीतर चलाया जाए, आपको केवल await Promise.resolve() करना है। यह अनिवार्य रूप से PromiseJobs कतार के अंत में परीक्षण के शेष भाग को कतारबद्ध करेगा और कतार में पहले से ही सब कुछ पहले से चलने देगा।

इस बात को ध्यान में रखते हुए, यहाँ परीक्षण का एक कार्यशील संस्करण है:

jest.useFakeTimers() 

it('simpleTimer', async () => {
  async function simpleTimer(callback) {
    await callback();
    setTimeout(() => {
      simpleTimer(callback);
    }, 1000);
  }

  const callback = jest.fn();
  await simpleTimer(callback);
  for(let i = 0; i < 8; i++) {
    jest.advanceTimersByTime(1000);
    await Promise.resolve(); // allow any pending jobs in the PromiseJobs queue to run
  }
  expect(callback).toHaveBeenCalledTimes(9);  // SUCCESS
});




jestjs