जेस्ट में फंक्शन नाम के फंक्शन को कैसे मॉक करें जब मॉड्यूल अनमॉक हो जाए


111

मेरे पास जेस्ट में परीक्षण करने के लिए निम्नलिखित मॉड्यूल हैं:

// myModule.js

export function otherFn() {
  console.log('do something');
}

export function testFn() {
  otherFn();

  // do other things
}

जैसा कि ऊपर दिखाया गया है, यह कुछ नामित कार्यों का निर्यात करता है और महत्वपूर्ण रूप से testFnउपयोग करता है otherFn

जेस्ट में जब मैं अपने यूनिट टेस्ट के लिए लिख रहा होता हूं testFn, तो मैं otherFnफंक्शन का मजाक उड़ाना चाहता हूं क्योंकि मैं नहीं चाहता कि otherFnमेरे यूनिट टेस्ट को प्रभावित करने के लिए त्रुटियां हों testFn। मेरा मुद्दा यह है कि मुझे ऐसा करने का सबसे अच्छा तरीका यकीन नहीं है:

// myModule.test.js
jest.unmock('myModule');

import { testFn, otherFn } from 'myModule';

describe('test category', () => {
  it('tests something about testFn', () => {
    // I want to mock "otherFn" here but can't reassign
    // a.k.a. can't do otherFn = jest.fn()
  });
});

किसी भी मदद / अंतर्दृष्टि की सराहना की है।


7
मैं ऐसा नहीं करूंगा। Mocking आम तौर पर कुछ भी नहीं है जो आप वैसे भी करना चाहते हैं। और अगर आपको किसी चीज का मजाक बनाने की जरूरत है (सर्वर कॉल / आदि करने के कारण।) तो आपको बस otherFnएक अलग मॉड्यूल निकालना चाहिए और उसका मजाक उड़ाना चाहिए।
kentcdodds

2
मैं भी उसी दृष्टिकोण @jrubins का उपयोग करता है के साथ परीक्षण कर रहा हूँ। function Aकौन कॉल करता है इसका परीक्षण व्यवहार function Bलेकिन मैं वास्तविक कार्यान्वयन को निष्पादित नहीं करना चाहता function Bक्योंकि मैं सिर्फ function A
20-30 पर jplaza

44
@kentcdodds, क्या आप स्पष्ट कर सकते हैं कि आपके द्वारा "मॉकिंग का मतलब आमतौर पर कुछ भी नहीं है जो आप वैसे भी करना चाहते हैं?" यह एक काफी व्यापक (व्यापक रूप से व्यापक?) कथन प्रतीत होता है, जैसा कि निश्चित रूप से मॉकिंग निश्चित रूप से उपयोग किया जाता है, संभवतः (कम से कम कुछ) अच्छे कारणों के लिए। तो, क्या आप शायद इस बात का जिक्र कर रहे हैं कि यहाँ मज़ाक करना अच्छा क्यों नहीं हो सकता है , या आप वास्तव में सामान्य रूप से मतलब रखते हैं?
एंड्रयू विल्म्स

2
अक्सर मॉकिंग कार्यान्वयन विवरण का परीक्षण कर रहा है। विशेष रूप से इस स्तर पर यह उन परीक्षणों की ओर जाता है जो वास्तव में इस तथ्य से अधिक मान्य नहीं हैं कि आपके परीक्षण काम करते हैं (यह नहीं कि आपका कोड काम करता है)।
kentcdodds

3
रिकॉर्ड के लिए, यह प्रश्न वर्षों पहले लिखने के बाद से, मैंने तब से अपनी धुन बदल दी है कि मैं कितना मजाक करना चाहूंगा (और अब इस तरह का मजाक नहीं करता)। इन दिनों मैं @kentcdodds और उनके परीक्षण दर्शन से बहुत सहमत हूं (और अपने ब्लॉग की सिफारिश करता @testing-library/reactहूं और वहां से बाहर किसी भी रिएक्टर के लिए) लेकिन मुझे पता है कि यह एक विवादास्पद विषय है।
जॉन रुबिन्स

जवाबों:


100

jest.requireActual()अंदर का उपयोग करेंjest.mock()

jest.requireActual(moduleName)

एक नकली के बजाय वास्तविक मॉड्यूल लौटाता है, इस पर सभी जांचों को दरकिनार करता है कि मॉड्यूल को एक नकली कार्यान्वयन प्राप्त करना चाहिए या नहीं।

उदाहरण

मैं इस संक्षिप्त उपयोग को पसंद करता हूँ जहाँ आपको आवश्यकता होती है और लौटी हुई वस्तु के भीतर फैलता है:

// myModule.test.js

jest.mock('./myModule.js', () => (
  {
    ...(jest.requireActual('./myModule.js')),
    otherFn: jest.fn()
  }
))

import { otherFn } from './myModule.js'

describe('test category', () => {
  it('tests something about otherFn', () => {
    otherFn.mockReturnValue('foo')
    expect(otherFn()).toBe('foo')
  })
})

यह विधि जेस्ट के मैनुअल मोक्स प्रलेखन ( उदाहरणों के अंत के पास ) में भी संदर्भित है :

यह सुनिश्चित करने के लिए कि एक मैनुअल मॉक और इसका वास्तविक कार्यान्वयन सिंक में रहता है, यह jest.requireActual(moduleName)आपके मैनुअल मॉक में वास्तविक मॉड्यूल का उपयोग करने और इसे निर्यात करने से पहले नकली कार्यों के साथ संशोधन करने के लिए उपयोगी हो सकता है।


4
Fwiw, आप returnस्टेटमेंट को हटाकर और कोष्ठक में एरो फंक्शन बॉडी को लपेट कर इसे और भी संक्षिप्त बना सकते हैं : उदा। jest.mock('./myModule', () => ({ ...jest.requireActual('./myModule'), otherFn: () => {}}))
निक एफ

2
यह महान काम किया! यह स्वीकृत उत्तर होना चाहिए।
JRJurman

2
...jest.requireActualमेरे लिए ठीक से काम नहीं किया क्योंकि मेरे पास ...require.requireActual
राहेल

1
आप otherFunइस मामले में कैसे परीक्षण करेंगे ? मान लियाotherFn: jest.fn()
स्टेवुला

1
@Stevula मैंने वास्तविक उपयोग उदाहरण दिखाने के लिए अपना उत्तर अपडेट कर दिया है। मैं mockReturnValueबेहतर प्रदर्शन करने के लिए विधि दिखाता हूं कि मूल के बजाय नकली संस्करण को कॉल किया जा रहा है, लेकिन अगर आप वास्तव में सिर्फ यह देखना चाहते हैं कि क्या इसे रिटर्न वैल्यू के खिलाफ जोर दिए बिना बुलाया गया है, तो आप जेस्ट मैचर का उपयोग कर सकते हैं .toHaveBeenCalled()
गुरफलाम

36
import m from '../myModule';

मेरे लिए काम नहीं करता है, मैंने इस्तेमाल किया:

import * as m from '../myModule';

m.otherFn = jest.fn();

5
आप परीक्षण के बाद दूसरे की मूल कार्यक्षमता को कैसे पुनर्स्थापित करेंगे ताकि यह अन्य परीक्षण के साथ हस्तक्षेप न करे?
असितात्स

1
मुझे लगता है कि आप हर टेस्ट के बाद जेक को क्लियर करने के लिए जेस्ट कॉन्फ़िगर कर सकते हैं? डॉक्स से: "क्लिक्स के बीच स्वतः ही क्लियर करने के लिए क्लीयरॉक्स कॉन्फ़िगरेशन विकल्प उपलब्ध है।" आप clearkMocks: truejest package.json config सेट कर सकते हैं । facebook.github.io/jest/docs/en/mock-function-api.html
कोल

2
अगर यह वैश्विक स्थिति को बदलने के लिए ऐसी समस्या है, तो आप हमेशा किसी प्रकार के परीक्षण चर के अंदर मूल कार्यक्षमता को स्टोर कर सकते हैं और परीक्षण के बाद इसे वापस ला सकते हैं
bobu

1
const मूल; पहलेAll () => {मूल = m.otherFn; m.otherFn = jest.fn ();}) afterAll (() => {m.otherFn = original;}) यह काम करना चाहिए, हालांकि मैंने परीक्षण नहीं किया था यह
बोबू

आपको बहुत - बहुत धन्यवाद! इससे मेरी समस्या हल हो गई।
स्लोबोदान क्रैसावेविक

25

ऐसा लगता है कि मुझे इस पार्टी में देर हो रही है, लेकिन हां, यह संभव है।

testFnबस otherFn मॉड्यूल का उपयोग कर कॉल करने की जरूरत है ।

यदि testFnकॉल करने के लिए मॉड्यूल का उपयोग करता है, otherFnतो इसके लिए मॉड्यूल निर्यात को मॉक otherFnकिया जा सकता है और testFnमॉक को कॉल करेगा।


यहाँ एक काम कर उदाहरण है:

myModule.js

import * as myModule from './myModule';  // import myModule into itself

export function otherFn() {
  return 'original value';
}

export function testFn() {
  const result = myModule.otherFn();  // call otherFn using the module

  // do other things

  return result;
}

myModule.test.js

import * as myModule from './myModule';

describe('test category', () => {
  it('tests something about testFn', () => {
    const mock = jest.spyOn(myModule, 'otherFn');  // spy on otherFn
    mock.mockReturnValue('mocked value');  // mock the return value

    expect(myModule.testFn()).toBe('mocked value');  // SUCCESS

    mock.mockRestore();  // restore otherFn
  });
});

2
यह अनिवार्य रूप से फेसबुक पर उपयोग किए जाने वाले दृष्टिकोण का ईएस 6 संस्करण है और इस पोस्ट के बीच में एक फेसबुक देव द्वारा वर्णित है ।
ब्रायन एडम्स

खुद में myModule आयात करने के बजाय, बस फोनexports.otherFn()
andrhamm

3
@ 6 में ईश्वरम का exportsअस्तित्व नहीं है। exports.otherFn()अभी कॉलिंग काम करती है क्योंकि ES6 को पहले वाले मॉड्यूल सिंटैक्स में संकलित किया जा रहा है, लेकिन जब ES6 को मूल रूप से समर्थन किया जाता है तो यह टूट जाएगा।
ब्रायन एडम्स

अभी इस सटीक समस्या के होने और मुझे यकीन है कि मैंने पहले भी इस समस्या का सामना किया है। मुझे निर्यात का एक लोड हटाना पड़ा है। <पद्धति>> पेड़ को हिलाने में मदद करने के लिए और यह कई परीक्षणों को तोड़ चुका है। मैं देखूंगा कि क्या इसका कोई प्रभाव है, लेकिन ऐसा लगता है कि यह बहुत ही हैकिंग है। मैं कई बार इस समस्या को लेकर आया हूं और अन्य उत्तरों की तरह बाबेल-प्लगइन- रीवायर या उससे भी बेहतर, npmjs.com/package/rewiremock जैसी चीजें जो मुझे यकीन है कि उपरोक्त भी कर सकते हैं।
16

क्या एक वापसी मूल्य का मजाक उड़ाने के बजाय, एक फेंक का मजाक करना संभव है? संपादित करें: आप कर सकते हैं, इस तरह है stackoverflow.com/a/50656680/2548010
बिग मनी

10

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

// myModule.js
exports.otherFn = () => {
  console.log('do something');
}

exports.testFn = () => {
  exports.otherFn();

  // do other things
}

 

// myModule.test.js
import m from '../myModule';

m.otherFn = jest.fn();

लेकिन जैसा कि @kentcdodds ने पिछली टिप्पणी में बताया है, आप शायद इसका मजाक नहीं उड़ाना चाहेंगे otherFn()। बल्कि, बस एक नई युक्ति लिखें otherFn()और जो भी आवश्यक कॉल कर रहा है, उसका मजाक उड़ाएं।

उदाहरण के लिए, यदि otherFn()एक http अनुरोध कर रहा है ...

// myModule.js
exports.otherFn = () => {
  http.get('http://some-api.com', (res) => {
    // handle stuff
  });
};

यहां, आप http.getअपने नकली कार्यान्वयन के आधार पर अपने दावे का मजाक बनाना और अद्यतन करना चाहेंगे ।

// myModule.test.js
jest.mock('http', () => ({
  get: jest.fn(() => {
    console.log('test');
  }),
}));

1
क्या होगा अगर otherFn और testFn का उपयोग कई अन्य मॉड्यूल द्वारा किया जाता है? क्या आपको उन सभी टेस्ट फाइलों में http मॉक सेट करने की आवश्यकता होगी जो उन 2 मॉड्यूल का उपयोग (हालांकि स्टैक को गहरा) करते हैं? इसके अलावा, यदि आपके पास पहले से ही TestFn के लिए एक परीक्षण है, तो TestFn का उपयोग करने वाले मॉड्यूल में http के बजाय testFn को सीधे स्टब क्यों नहीं किया जाता है?
rickmed

1
तो अगर otherFnटूट गया है, तो उस एक पर निर्भर सभी परीक्षणों को विफल कर देगा। यदि otherFnआपके अंदर 5 भी हैं तो आपको यह परखना होगा कि आपके testFnसभी उप मामलों के लिए ठीक काम कर रहा है। अब आपके पास परीक्षण करने के लिए बहुत अधिक कोड पथ होंगे।
Totty.js

6

मुझे पता है कि यह एक लंबे समय से पहले पूछा गया था, लेकिन मैं बस इस स्थिति में भाग गया और अंत में एक समाधान मिला जो काम करेगा। इसलिए मैंने सोचा कि मैं यहाँ साझा करूँगा।

मॉड्यूल के लिए:

// myModule.js

export function otherFn() {
  console.log('do something');
}

export function testFn() {
  otherFn();

  // do other things
}

आप निम्नलिखित को बदल सकते हैं:

// myModule.js

export const otherFn = () => {
  console.log('do something');
}

export const testFn = () => {
  otherFn();

  // do other things
}

कार्यों के बजाय उन्हें एक स्थिरांक के रूप में निर्यात करना। मेरा मानना ​​है कि इस मुद्दे को जावास्क्रिप्ट में उत्थापन के साथ करना है और constउस व्यवहार को रोकता है।

फिर आपके परीक्षण में आपके पास निम्नलिखित जैसे कुछ हो सकते हैं:

import * as myModule from 'myModule';


describe('...', () => {
  jest.spyOn(myModule, 'otherFn').mockReturnValue('what ever you want to return');

  // or

  myModule.otherFn = jest.fn(() => {
    // your mock implementation
  });
});

आपके मोक्स को अब काम करना चाहिए जैसा कि आप आमतौर पर उम्मीद करते हैं।


2

मैंने अपनी समस्याओं को उन उत्तरों के मिश्रण से हल किया जो मुझे यहाँ मिले:

myModule.js

import * as myModule from './myModule';  // import myModule into itself

export function otherFn() {
  return 'original value';
}

export function testFn() {
  const result = myModule.otherFn();  // call otherFn using the module

  // do other things

  return result;
}

myModule.test.js

import * as myModule from './myModule';

describe('test category', () => {
  let otherFnOrig;

  beforeAll(() => {
    otherFnOrig = myModule.otherFn;
    myModule.otherFn = jest.fn();
  });

  afterAll(() => {
    myModule.otherFn = otherFnOrig;
  });

  it('tests something about testFn', () => {
    // using mock to make the tests
  });
});

0

यहां पहले उत्तर के शीर्ष पर, आप बेबल-प्लगइन-रीवायर का उपयोग कर सकते हैं, नामांकित फ़ंक्शन को भी नकली करने के लिए। नामांकित फ़ंक्शन के लिए आप सतही रूप से अनुभाग देख सकते हैं ।

यहां आपकी स्थिति के लिए तत्काल लाभों में से एक यह है कि आपको अपने फ़ंक्शन से दूसरे फ़ंक्शन को कॉल करने के तरीके को बदलने की आवश्यकता नहीं है।


नोड.जेएस के साथ काम करने के लिए बैबल-प्लगइन-रीवायर को कैसे कॉन्फ़िगर करें?
तैमूर गिलौरी
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.