Jest: كيف تسخر من وظيفة وحدة معينة؟

تم إنشاؤها على ٢٥ أبريل ٢٠١٦  ·  116تعليقات  ·  مصدر: facebook/jest

أنا أعاني من شيء أعتقد أنه يجب أن يكون سهلاً وواضحًا ، لكن لأي سبب لا يمكنني اكتشافه.

لدي وحدة. يقوم بتصدير وظائف متعددة. هنا myModule.js :

export function foo() {...}
export function bar() {...}
export function baz() {...}

أنا أفتح الوحدة للاختبار.

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

ومع ذلك ، أحتاج إلى السخرية من foo ، لأنه يقوم بإجراء مكالمات ajax إلى الواجهة الخلفية الخاصة بي. أريد أن تظل كل وظيفة في هذا الملف غير مهزومة ، وتوقع أن يكون سعرها foo ، وهو ما أريد أن يتم السخرية منه. وتقوم الدالتان bar و baz بإجراء مكالمات داخليًا إلى foo ، لذلك عندما يستدعي اختباري bar() ، فإن bar المهزوم سيستدعي سخر من foo .

يظهر في وثائق الدعابة أن المكالمات إلى unmock و mock تعمل على الوحدة بأكملها. كيف يمكنني أن أسخر من وظيفة معينة؟ إن تقسيم الكود الخاص بي بشكل تعسفي إلى وحدات منفصلة بحيث يمكن اختبارها بشكل صحيح أمر مثير للسخرية.

التعليق الأكثر فائدة

يمكنك ان تفعل:

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

const myModule = require('myModule');
myModule.foo = jest.fn();

راجع http://facebook.github.io/jest/docs/api.html#mock -functions

ال 116 كومينتر

بناءً على تحليل أعمق ، يبدو أن jest-mock يولد AST للوحدة بأكملها ، ثم يستخدم ذلك AST لإنشاء وحدة تم الاستهزاء بها تتوافق مع الصادرات الأصلية: https://github.com/facebook/jest/tree/master/packages / jest-mock

تتيح لك أطر عمل الاختبار الأخرى ، مثل Python mock (https://docs.python.org/3/library/unittest.mock-examples.html) ، محاكاة وظائف معينة. هذا هو مفهوم الاختبار الأساسي.

أوصي بشدة بالقدرة على الاستهزاء بجزء من الوحدة. أعتقد أنه يجب تغيير الدعابة الساخرة لتجاهل الصادرات من السخرية المشروط ، والإشارة إلى التنفيذ الأصلي.

يمكنك ان تفعل:

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

const myModule = require('myModule');
myModule.foo = jest.fn();

راجع http://facebook.github.io/jest/docs/api.html#mock -functions

أعتقد أن لديك سوء فهم أساسي لكيفية عمل require . عند استدعاء require() ، لن تحصل على مثيل للوحدة. تحصل على كائن مع إشارات إلى وظائف الوحدة. إذا قمت بالكتابة فوق قيمة في الوحدة النمطية المطلوبة ، فسيتم استبدال المرجع الخاص بك ، ولكن التطبيق يحتفظ بالمراجع الأصلية.

في مثالك ، إذا اتصلت بـ myModule.foo() ، نعم ، ستتصل بالإصدار الذي تم الاستهزاء به. ولكن إذا اتصلت بـ myModule.bar() ، والذي يستدعي داخليًا foo() ، فإن foo يشير إليه _ ليس نسختك المكتوبة_. إذا كنت لا تصدقني ، يمكنك اختبار ذلك.

لذلك ، فإن المثال الذي وصفته غير مناسب للمشكلة التي لدي. هل تعرف شيئاً لا أعرفه؟

تضمين التغريدة

أعتقد أنني أفهم هذا جيدًا. ومع ذلك ، فإن الطريقة التي يجمع بها بابل الوحدات النمطية لا تجعل هذا الأمر أسهل في الفهم وأنا أفهم حيرتك. لا أعرف بالضبط كيف سيتصرف هذا في بيئة ES2015 حقيقية مع وحدات ، ويرجع ذلك أساسًا إلى عدم وجود مثل هذه البيئة في الوقت الحالي (ربما باستثناء أحدث إصدارات Chrome Canary ، والتي لم أجربها بعد). من أجل شرح ما يحدث ، علينا أن ننظر إلى المخرجات المترجمة لشفرة بابل الخاصة بك. سيبدو شيئا من هذا القبيل:

var foo = function foo() {};
var bar = function bar() { foo(); };

exports.foo = foo;
exports.bar = bar;

في هذه الحالة ، من الصحيح حقًا أنه لا يمكنك السخرية من foo وأنا أعتذر عن عدم قراءة مشكلتك الأولية بشكل صحيح ، ومع ذلك لم يقم بأي افتراض حول كيفية استدعاء foo ، لذلك افترضت أنه exports.foo() . يعد دعم ما سبق من خلال الاستهزاء بوظيفة ما بعد طلب وحدة نمطية أمرًا مستحيلًا في JavaScript - لا توجد (تقريبًا) طريقة لاسترداد الارتباط الذي يشير إليه foo وتعديله.

ومع ذلك ، إذا قمت بتغيير الرمز الخاص بك إلى هذا:

var foo = function foo() {};
var bar = function bar() { exports.foo(); };

exports.foo = foo;
exports.bar = bar;

ثم في ملف الاختبار الخاص بك ، قم بما يلي:

var module = require('../module');
module.foo = jest.fn();
module.bar();

ستعمل كما هو متوقع. هذا ما نفعله في Facebook حيث لا نستخدم ES2015.

في حين أن وحدات ES2015 قد تحتوي على ارتباطات غير قابلة للتغيير لما تصدره ، إلا أن الكود الأساسي المترجم الذي يقوم بابل بجمعه في الوقت الحالي لا يفرض أي قيود من هذا القبيل. لا أرى طريقة حاليًا لدعم ما تطلبه بالضبط في بيئة وحدة ES2015 صارمة مع وحدات مدعومة أصلاً. الطريقة التي يعمل بها jest-mock هي تشغيل رمز الوحدة بمعزل ثم استرداد البيانات الوصفية للوحدة النمطية وإنشاء وظائف وهمية. مرة أخرى ، في هذه الحالة ، لن يكون هناك أي طريقة لتعديل الارتباط المحلي لـ foo . إذا كانت لديك أفكار حول كيفية تنفيذ ذلك بشكل فعال ، فيرجى المساهمة هنا أو مع طلب سحب. أود أن أذكرك بأن لدينا مدونة سلوك لهذا المشروع يمكنك قراءتها هنا: https://code.facebook.com/pages/876921332402685/open-source-code-of-conduct

الحل الصحيح في المثال الخاص بك ليس السخرية من foo ولكن للسخرية من واجهة برمجة التطبيقات ذات المستوى الأعلى التي يستدعيها foo (مثل XMLHttpRequest أو التجريد الذي تستخدمه بدلاً من ذلك).

cpojer شكرًا لك على الشرح التفصيلي. أنا آسف إذا أزعجتك لغتي ، فأنا فعال للغاية في كتابتي الهندسية وأريد أن أوضح وجهة نظري في أسرع وقت ممكن. لوضع الأمور في نصابها الصحيح ، أمضيت 5 ساعات من الوقت في محاولة فهم هذه المشكلة وكتبت تعليقين تفصيليين ، ثم قمت بإغلاقها برسالة موجزة فاتت تمامًا الهدف من كلا البيانين اللذين أدليت بهما. هذا هو السبب في رسالتي التالية قالت إن لديك "سوء فهم أساسي" ، لأنه إما 1) أنك لم تفهم النقطة التي كنت أثيرها ، أو 2) لم تفهم require() ، والذي كان لحسن الحظ الخيار 1.

سوف أفكر في حل ممكن لمشكلتي ، للتغلب عليها في الوقت الحالي ، لقد سخرت من واجهة برمجة التطبيقات ذات المستوى الأدنى ، ولكن يجب أن تكون هناك بالتأكيد طريقة للسخرية من الوظيفة مباشرة ، لأن ذلك سيكون مفيدًا للغاية.

أوافق على أنه سيكون من المفيد أن تكون قادرًا على القيام بذلك ، ولكن لا توجد طريقة جيدة في JS للقيام بذلك بدون تحليل ثابت (ربما بطيء) مقدمًا :(

cpojer : لست متأكدًا مما إذا كان القفز هنا بعد 5 أشهر هو السبيل للذهاب ولكن لم أجد أي محادثات أخرى حول هذا الموضوع.

قفزت من اقتراحك أعلاه ، لقد فعلت هذا للاستهزاء بوظيفة واحدة من أخرى في نفس الوحدة:

jest.unmock('./someModule.js');
import someModule from './someModule.js';

it('function1 calls function 2', () => {
    someModule.function2 = jest.fn();

    someModule.function1(...);

    expect(someModule.function2).toHaveBeenCalledWith(...);
});

يعمل هذا مع الاختبار الواحد ، لكنني لم أجد طريقة لتحقيق ذلك بطريقة معزولة عن الكتلة it(...); . كما هو مذكور أعلاه ، فإنه يؤثر على كل اختبار مما يجعل من الصعب اختبار function2 الحقيقي في اختبار آخر. أي نصائح؟

يمكنك استدعاء .mockClear على الوظيفة في beforeEach أو استدعاء jest.clearAllMocks() إذا كنت تستخدم Jest 16.

يا @ cpojer! أنا أستخدم Jest 16. لا يعمل لي أي من jest.clearAllMocks() أو someModule.function2.mockClear() . تعمل فقط عندما يكون النموذج الوهمي عبارة عن وحدة كاملة ، وليس دالة لوحدة مستوردة. في مشروعي ، ظلت الوظيفة تتعرض للسخرية في الاختبارات اللاحقة. إذا لم يكن هذا متوقعًا ، فسأرى ما إذا كان بإمكاني النسخ المتماثل في مشروع نموذج صغير وإنشاء مشكلة جديدة. فكره جيده؟

cpojer -

الحل الصحيح في المثال الخاص بك ليس السخرية من foo ولكن للسخرية من واجهة برمجة التطبيقات ذات المستوى الأعلى التي يستدعيها foo (مثل XMLHttpRequest أو التجريد الذي تستخدمه بدلاً من ذلك).

أنا جديد في Jest وأواجه مشكلة مماثلة. أنا باستخدام أكسيوس ، التي تحت غطاء محرك السيارة يستخدم XMLHttpRequest ، وأنا لا أريد أن يسخر axios ، ولكن للسخرية الفعلية XMLHttpRequest . يبدو أنني سأضطر إلى تطبيق أساليبه بنفسي ، شيء من هذا القبيل. هل هذا هو النهج الصحيح؟

شكرا!

نعم ، شيء من هذا القبيل يجب أن يضعك على الطريق الصحيح! :) استخدم jest.fn كواجهة برمجة تطبيقات أجمل ، على الرغم من: د

cpojer بخصوص تعليقك هنا: https://github.com/facebook/jest/issues/936#issuecomment -214939935

كيف ستفعل ذلك مع ES2015؟

// myModyle.js
export foo = (string) => "foo-" + string
export bar = (string2) => foo(string2)

// myModule.test.js
var module = require('./myModule');

// how do I mock foo here so this test passes?
expect(bar("hello")).toEqual("test-hello")

بالنسبة لأي شخص يصادف هذا البحث عن حل ، يبدو أن ما يلي يعمل بالنسبة لي عند تصدير العديد من الدوال / const في ملف واحد ، واستيرادها في ملف أقوم باختباره

" javascript function mockFunctions() { const original = require.requireActual('../myModule'); return { ...original, //Pass down all the exported objects test: jest.fn(() => {console.log('I didnt call the original')}), someFnIWantToCurry: {console.log('I will curry the original') return jest.fn((...args) => original.someFnIWantToCurry(...args)}), } jest.mock('../myModule', () => mockFunctions()); const storage = require.requireMock('../myModule');

ainesophaur ، لست متأكدًا مما أفعله خطأ هنا. لكن يبدو أنه لا يعمل
أنا حاليًا على jest 18.1 (و create-response-app 0.9.4)

...<codes from comment above>..

// Let's say the original myModule has a function doSmth() that calls test()
storage.doSmth();
expect(storage.test).toHaveBeenCalled();

سيفشل الاختبار بعد ذلك مع:

expect(jest.fn()).toHaveBeenCalled()
Expected mock function to have been called.

huyph ، عليك أن تسخر من طريقة doSmth وطريقة الاختبار حتى تختبر الدعابة ما إذا كان قد تم استدعاؤها. إذا كان بإمكانك تقديم مقتطف من الشفرة الساخرة ، يمكنني التحقق من الخطأ

تضمين التغريدة اعتقدت أن الرموز الخاصة بك أعلاه للسخرية من طريقة test() ؟ هذا الجزء: test: jest.fn(() => {console.log('I didnt call the original')}),

ainesophaur لقد جربت الكود أيضًا. لكنها لم تعمل بالنسبة لي. لا ينفذ أبدا وظيفة وهمية. لذا ، فإن التوقعات لا تتحقق أبدًا.

أعتقد أن هذا متأصل في الطريقة التي تتطلب أعمالًا مثل المذكورة أعلاه ... أتمنى أن يكون هناك حل لذلك.

cpojer هل هناك أي جديد بخصوص وحدات الاستهزاء الجزئي؟

rantonmattei & huyph يجب أن أرى مقتطفًا من التعريفات الوهمية والاختبار الذي تقوم بتشغيله. يجب عليك تحديد النموذج الخاص بك قبل أن يكون ملف التنفيذ الفعلي مطلوبًا / مستوردًا. لقد مر وقت طويل منذ أن عملت مع JEST ، لكنني أتذكر أنني حصلت عليه في النهاية للسخرية من كل ما أحتاج إليه ، سواء كان مكتبة node_modules أو ملفًا في تطبيقي. لديّ وقت قصير جدًا في أجهزة الصراف الآلي ، ولكن إليك بعض الاختبارات من مشروع عملت عليه باستخدام Jest.

الاستهزاء بملف من تبعية

تم تعريف الوظيفة الفعلية في هذا المثال بواسطة رد فعل أصلي .. أنا أسخر من الملف "رد فعل أصلي / مكتبات / أدوات مساعدة / رفض Keyboard.js"

هذا ملف وهمي تحت __mocks__/react-native/Libraries/Utilities/dismissKeyboard.js

function storeMockFunctions() {
  return jest.fn().mockImplementation(() => true);
}
jest.mock('react-native/Libraries/Utilities/dismissKeyboard', () => storeMockFunctions(), { virtual: true });
const dismissKeyboard = require('react-native/Libraries/Utilities/dismissKeyboard');
exports = module.exports = storeMockFunctions;

لا يمكنني العثور على ملف الاختبار الذي استخدمته لما سبق ، ولكنه كان شيئًا مثل طلب الوحدة ، ومازحًا ستجده في __الخزائن__ وبعد ذلك يمكنني فعل شيء مثل

expect(dismissKeyboard.mock.calls).toHaveLength(1);

الاستهزاء بملف تتحكم فيه
تعريف الوظيفة الفعلية

export const setMerchantStores = (stores) => storage.set('stores', stores);

ملف اختبار مع وهمية

const { storeListEpic, offerListEpic } = require('../merchant');

function storeMockFunctions() {
  const original = require.requireActual('../../common/Storage');
  return {
    ...original,
    setMerchantStores: jest.fn((...args) => original.setMerchantStores(...args)),
    setMerchantOffers: jest.fn((...args) => original.setMerchantOffers(...args)),
  };
}
jest.mock('../../common/Storage', () => storeMockFunctions());
import * as storage from '../../common/Storage';

afterEach(() => {
  storage.setMerchantStores.mockClear();
});

it('handle storeListEpic type STORE_LIST_REQUEST -> STORE_LIST_SUCCESS', async () => {
  const scope = nock('http://url')
  .get('/api/merchant/me/stores')
  .reply(200, storeData);
  const result = await storeListEpic(ActionsObservable.of(listStores())).toPromise();
  expect(storage.setMerchantStores.mock.calls).toHaveLength(1);
  expect(await storage.getMerchantStores()).toEqual({ ids: storesApiData.result, list: storesApiData.entities.store});
});

شكرا لمشاركةainesophaur. ما زلت لا أستطيع الحصول عليها للعمل مع المزاح 18.1. ها هي أكوادي:

it('should save session correctly', () => {

  function mockFunctions() {
    const original = require.requireActual('./session');
    return {
      ...original,
      restartCheckExpiryDateTimeout: jest.fn((() => {
        console.log('I didn\'t call the original');
      })),
    }
  }

  jest.mock('./session', () => mockFunctions());
  const mockSession = require('./session');

  // NOTE: saveSession() will call the original restartCheckExpiryDateTimeout() instead of my
  // mock one. However, mockSession.restartCheckExpiryDateTimeout() does call the mock one
  mockSession.saveSession('', getTomorrowDate(), 'AUTH');

  // mockSession.restartCheckExpiryDateTimeout(); // does print out "I didn't call the original"

  expect(mockSession.restartCheckExpiryDateTimeout).toHaveBeenCalled();
});

في session.js

export function saveSession(sessionId, sessionExpiryDate, authToken) {
  ....
  restartCheckExpiryDateTimeout(sessionExpiryDate);
  ...
}
....

export function restartCheckExpiryDateTimeout(...) {
....
}

لا يمكنني إيجاد طريقة لحل هذه المشكلة. هل يمكنني إعادة فتح هذا من فضلك؟ تضمين التغريدة

huyph الطريقة التي تقوم بها بالتصدير saveSession ستستدعي restartCheckExpiryDateTimeout المعرّف محليًا بدلاً من الانتقال إلى الوحدة واستدعاء module.restartCheckExpiryDateTimeout - وبالتالي ، سخر منك module.restartCheckExpiryDateTimeout لن يتم الكشف عن saveSession لأن saveSession يستدعي الوظيفة المحددة الفعلية restartCheckExpiryDateTimeout .

كنت أقوم بتعيين saveSession لثابت ثم أقوم بـ saveSession.restartCheckExpiryDateTimeout = () => {...logic} . ثم من داخل saveSession.saveSession ، اتصل بـ saveSession.restartCheckExpiryDateTimeout بدلاً من restartCheckExpiryDateTimeout . قم بتصدير ثابتك الجديد بدلاً من الوظيفة الفعلية saveSession التي تحدد بعد ذلك طرقك. ثم عندما تتصل بـ someVar.saveSession() الخاص بك ، فسوف تتصل داخليًا بـ saveSession.restartCheckExpiryDateTimeout() وهو الأمر الذي يتم الاستهزاء به الآن.

كان يجب أن أضيف أن restartCheckExpiryDateTimeout() هي دالة مُصدَّرة. ليست وظيفة محددة محليًا ضمن saveSession() ... (تم تحديث تعليقي أعلاه). في هذه الحالة ، أعتقد أن module.saveSession() يجب أن يطلق على الحق module.restartCheckExpiryDateTimeout() الذي يتم الاستهزاء به.

لكنني سأقدم ما تقترحه أعلاه. نقل كل من saveSession() و restartCheckExpiryDateTimeout() إلى ثابت آخر. شكرا

أفهم أنه لم يتم تعريفه في نطاق جلسة الحفظ. saveSession هو
استدعاء طريقة الأخ في نطاق الوالدين. جريت في هذا مرات عديدة
وما اقترحته نجح في ذلك

في 8 مايو 2017 الساعة 8:38 مساءً ، كتب "Huy Pham" [email protected] :

كان يجب أن أضيف أن resetCheckExpiryDateTimeout () هو ملف
وظيفة. ليست وظيفة محددة محليًا ضمن saveSession () ...

سأقدم ما تقترحه أعلاه بالرغم من ذلك. شكرا

-
أنت تتلقى هذا لأنه تم ذكرك.
قم بالرد على هذا البريد الإلكتروني مباشرة ، وقم بعرضه على GitHub
https://github.com/facebook/jest/issues/936#issuecomment-300029003 ، أو كتم الصوت
الخيط
https://github.com/notifications/unsubscribe-auth/AEeBdsmpOOmzvcUHB3D_-Z7MChIzt10Pks5r37WYgaJpZM4IPGAH
.

لقد حاولت للتو .. لقد وجدت ما يلي:

هذا لا يعمل: (على سبيل المثال ، لا يزال يتم استدعاء إعادة التشغيل الأصلية ())

export session = {
   saveSession: () => {
      session.restartCheckExpiryDateTimeout();
   },
   restartCheckExpiryDateTimeout: () => {},
}

يعمل هذا: (على سبيل المثال ، يتم استدعاء إعادة التشغيل الوهمية () بدلاً من ذلك). الاختلاف هو استخدام function() بدلاً من شكل السهم ، واستخدام this. بدلاً من session.

export session = {
   saveSession: function() {
      this.restartCheckExpiryDateTimeout();
   },
   restartCheckExpiryDateTimeout: () => {},
}

قد تكون مشكلة في نقل هذه الرموز المزحة ...

حاول تصديرها ككائن فئة بدلاً من بوجو. أعتقد أن
يقوم transpiler برفع المتغيرات بشكل مختلف. سوف نصل إلى العمل
test ، أعدك .. لقد مر حوالي نصف عام منذ أن كنت في المشروع
التي استخدمت الدعابة لكنني أتذكر هذه المشكلة جيدًا وأتذكرها في النهاية
إيجاد حل.

في 9 مايو 2017 ، الساعة 12:53 صباحًا ، كتب "Huy Pham" [email protected] :

لقد حاولت للتو .. لقد وجدت ما يلي:

هذا لا يعمل: (على سبيل المثال ، إعادة التشغيل الأصلية) هي
لا يزال يتم الاتصال)

جلسة التصدير = {
saveSession: () => {
session.restartCheckExpiryDateTimeout () ،
} ،
resetCheckExpiryDateTimeout: () => {} ،
}

هذا لا يعمل: (على سبيل المثال ، إعادة التشغيل الأصلية () هي
لا يزال يتم الاتصال)

جلسة التصدير = {
saveSession: function () {
this.restartCheckExpiryDateTimeout () ،
} ،
resetCheckExpiryDateTimeout: () => {} ،
}

قد تكون مشكلة في نقل هذه الرموز المزحة ...

-
أنت تتلقى هذا لأنه تم ذكرك.
قم بالرد على هذا البريد الإلكتروني مباشرة ، وقم بعرضه على GitHub
https://github.com/facebook/jest/issues/936#issuecomment-300060975 ، أو كتم الصوت
الخيط
https://github.com/notifications/unsubscribe-auth/AEeBdrRQExycPYiGtvm7qYi5G87w6b6Oks5r3_FlgaJpZM4IPGAH
.

sorahn نفس المشكلة. es6 + babel ، كيف تسخر؟
cpojer هل هذا يعني es6 + babel ، export const function xx() {} ، تصدير العديد من الوظائف ، Jest ليس لديه طريقة للسخرية من وظيفة في وحدة نمطية (ملف) تسمى بواسطة وظيفة أخرى في نفس الوحدة (ملف)؟ أختبرها ، يبدو أنني على صواب. فقط لنمط commonjs ، يمكن لـ Jest أن يسخر من الوظيفة بنجاح ، مثل مثالك.

ainesophaur لا يعمل.

وحدة:

export const getMessage = (num: number): string => {
  return `Her name is ${genName(num)}`;
};

export function genName(num: number): string {
  return 'novaline';
}

اختبار:

function mockFunctions() {
  const original = require.requireActual('../moduleA');
  return {
    ...original,
    genName: jest.fn(() => 'emilie')
  }
}
jest.mock('../moduleA', () => mockFunctions());
const moduleA = require('../moduleA');

describe('mock function', () => {

  it('t-0', () => {
    expect(jest.isMockFunction(moduleA.genName)).toBeTruthy();
  })

  it('t-1', () => {

    expect(moduleA.genName(1)).toBe('emilie');
    expect(moduleA.genName).toHaveBeenCalled();
    expect(moduleA.genName.mock.calls.length).toBe(1);
    expect(moduleA.getMessage(1)).toBe('Her name is emilie');
    expect(moduleA.genName.mock.calls.length).toBe(2);

  });

});

نتيجة الاختبار:

FAIL  jest-examples/__test__/mock-function-0.spec.ts
  ● mock function › t-1

    expect(received).toBe(expected)

    Expected value to be (using ===):
      "Her name is emilie"
    Received:
      "Her name is novaline"

      at Object.it (jest-examples/__test__/mock-function-0.spec.ts:22:35)
      at Promise.resolve.then.el (node_modules/p-map/index.js:42:16)

  mock function
    ✓ t-0 (1ms)
    ✕ t-1 (22ms)

Test Suites: 1 failed, 1 total
Tests:       1 failed, 1 passed, 2 total
Snapshots:   0 total
Time:        0.215s, estimated 1s

انظر إلى تعليقاتي القليلة الأخيرة أعلاه. على وجه التحديد الأخير. لك
تستدعي الطرق المُصدرة طريقة الأخوة المحددة محليًا مقابل طريقة
طريقة التصدير الفعلية (حيث يوجد النموذج الخاص بك)

في 31 مايو 2017 الساعة 2:00 صباحًا ، كتب "novaline" [email protected] :

ainesophaur https://github.com/ainesophaur لا يعمل.

وحدة:

تصدير const getMessage = (num: number): string => {
إرجاع Her name is ${genName(num)} ؛
} ؛
تصدير genName (num: number): سلسلة {
عودة "نوفالين" ؛
}

اختبار:

وظيفة mockFunctions () {
const original = need.requireActual ('../ moduleA')؛
إرجاع {
...أصلي،
genName: jest.fn (() => 'emilie')
}
} jest.mock ('../ moduleA'، () => mockFunctions ())؛ const moduleA = تتطلب ('../ moduleA')؛
وصف ("وظيفة وهمية" ، () => {

هو ('t-0'، () => {
توقع (jest.isMockFunction (moduleA.genName)). toBeTruthy () ؛
})

هو ('t-1'، () => {

expect(moduleA.genName(1)).toBe('emilie');
expect(moduleA.genName).toHaveBeenCalled();
expect(moduleA.genName.mock.calls.length).toBe(1);
expect(moduleA.getMessage(1)).toBe('Her name is emilie');
expect(moduleA.genName.mock.calls.length).toBe(2);

}) ؛

}) ؛

نتيجة الاختبار:

FAIL jest -amples / __ test __ / mock-function-0.spec.ts
● وظيفة وهمية ›t-1

expect(received).toBe(expected)

Expected value to be (using ===):
  "Her name is emilie"
Received:
  "Her name is novaline"

  at Object.it (jest-examples/__test__/mock-function-0.spec.ts:22:35)
  at Promise.resolve.then.el (node_modules/p-map/index.js:42:16)

وظيفة وهمية
✓ t-0 (1 مللي ثانية)
✕ t-1 (22 مللي ثانية)

مجموعات الاختبار: فشل 1 ، إجمالي 1
الاختبارات: 1 فشل ، 1 ناجح ، 2 في المجموع
لقطات: 0 المجموع
الوقت: 0.215 ثانية ، 1 ثانية

-
أنت تتلقى هذا لأنه تم ذكرك.
قم بالرد على هذا البريد الإلكتروني مباشرة ، وقم بعرضه على GitHub
https://github.com/facebook/jest/issues/936#issuecomment-305091749 ، أو كتم الصوت
الخيط
https://github.com/notifications/unsubscribe-auth/AEeBdv6SafXlTtKo3DNeFWhbL6gV9l0Gks5r_QHjgaJpZM4IPGAH
.

ainesophaur : لقد حاولت export class Session { } . وهو لا يعمل معي.

الطريقة الوحيدة التي تناسبني هي في تعليقي أعلاه: حيث يتم استخدام بناء الجملة function بدلاً من السهم () => . هنا:

export const session = {
   saveSession: function() {
      this.restartCheckExpiryDateTimeout();
   },
   restartCheckExpiryDateTimeout: () => {},
}

هذا في Jest 20.0.3

ما أفعله هو إنشاء غلاف ثابت للوظائف ، ثم تصدير هذا الغلاف (مثل تصدير fns). ثم داخل الوحدة النمطية استخدم fns.functionName ، وبعد ذلك يمكنني jest.fn () الدالة fns.functionName

عندما نكتب وظيفة وهمية لوحدة معرفة من قبل المستخدم والتي يتم كتابتها في الكتابة المطبوعة وعندما نسمي وظيفة mock هي الوظيفة الأصلية التي يغطيها تقرير التغطية لأننا نستدعي النسخة المزيفة من الوظيفة.

لدي وظيفتان تم استيرادهما في الأصل في الاختبارات كـ
import { getCurrentDate, getStartEndDatesForTimeFrame } from ./../_helpers/date';
كما ترى ، يعتمد getStartEndDatesForTimeFrame على getCurrentDate . من خلال الإعداد التالي ، يعمل الاختبار getCurrentDate بشكل جيد ويستخدم الإصدار الذي تم الاستهزاء به. من ناحية أخرى ، لسبب ما ، لا يستخدم الاختبار getStartEndDatesForTimeFrame السخرية منه getCurrentDate لكن التطبيق الأصلي لذلك فشل الاختبار. لقد جربت العديد من الإعدادات المختلفة (مثل Date.now = jest.fn(() => "2017-11-16T20:33:09.071Z"); لكن لم أستطع أن أجعلها تعمل. هل من أفكار؟

export const getCurrentDate = () => new Date();
export const getStartEndDatesForTimeFrame = (timeFrame) => {
  ...
  const todayDate = getCurrentDate();
  ...
  switch (timeframe) {
    case TimeFrames.TODAY:
      console.log(todayDate); // this always prints the real value in tests instead of the mocked one
      start = new Date(todayDate.getFullYear(), todayDate.getMonth(), todayDate.getDate(), 0, 0, 0);
      end = new Date(
        todayDate.getFullYear(),
        todayDate.getMonth(),
        todayDate.getDate(), 23, 59, 59,
      );
      break;
  ...
  return { start: start.toISOString(), end: end.toISOString() }
};
function mockFunctions() {
  const original = require.requireActual('../../_helpers/date');
  return {
    ...original,
    getCurrentDate: jest.fn(() => '2017-11-16T20:33:09.071Z'),
  }
}
jest.mock('../../_helpers/date', () => mockFunctions());
const dateModule = require.requireMock('../../_helpers/date');

describe('getCurrentDate', () => {
  it('returns the mocked date', () => {
    expect(dateModule.getCurrentDate()).
      toBe('2017-11-16T20:33:09.071Z'); // this works well and returns the mocked value
  });
});

describe('getStartEndDatesForTimeFrame', () => {
  it('returns the start and end dates for today', () => {
    expect(dateModule.getStartEndDatesForTimeFrame('today')).toEqual(
      { 'start': '2017-11-15T23:00:00.000Z', 'end': '2017-11-16T22:59:59.000Z' }
    ); // this one uses the original getCurrentDate instead of the mocked one :(
  });
});

لذلك فشل getStartEndDatesForTimeFrame لأنه يستخدم الوقت الحالي وليس الوقت الذي تم الاستهزاء به.

لقد تمكنت من جعله يعمل باتباع اقتراح من ainesophaur - عن طريق تصدير جميع الوظائف داخل كائن واستدعاء طرق الكائن المُصدَّر هذه بدلاً من طرق الأشقاء المحلية المحددة النطاق:

// imageModel.js
const model = {
  checkIfImageExists,
  getImageUrl,
  generateImagePreview
}
export default model

async function checkIfImageExists(...) {}
async function getImageUrl() {}
async function generateImagePreview() {
  // I am calling it as `model` object's method here, not as a locally scoped function
  return model.getImageUrl(...)
}

// imageModel.test.js
import imageModel from './imageModel'

test('should mock getImageUrl called within the same file', async () => {
  imageModel.getImageUrl = jest.fn().mockReturnValueOnce(Promise.resolve())

  await imageModel.generateImagePreview()

  expect(imageModel.getImageUrl).toBeCalled()
})

miluoshi هذه هي الطريقة الوحيدة التي تمكنت من القيام بها أيضًا. هل هناك أي خسارة في الأداء أو شيء من هذا القبيل عند استخدامنا لهذه الطريقة؟ يبدو أنه من "الخطأ" تغيير الرمز حتى تتمكن من اختباره.

أود حقًا طريقة للكتابة فقط:
jest.mock('src/folder/file.func, () => {return 'whatever i want'})

القطعة الرئيسية هنا هي .func

miluoshiRdlenke إذا يتكون التعليمات البرمجية الصادرات اسمه يمكنك أيضا import * as model ثم الكتابة model.generateImagePreview = jest.fn(() => Promise.resolve);

كيف تختبر ذلك مع sinon؟ كما ذكرنا سابقًا (راجع https://github.com/facebook/jest/issues/936#issuecomment-214939935) ، فإن الطريقة التي تعمل بها ESM تجعل من المستحيل الاستهزاء بـ func2 ضمن func1 ، لذلك لن أسميها بالضرورة أساسية.

ربما يمكن كتابة تعديل بابل يقرأ في أي وظائف "testImport"
ويعيد كتابة الكود لتصدير الوظائف في الوحدة النمطية قبل ملف
تشغيل الاختبار؟

في يوم الاثنين 18 ديسمبر 2017 الساعة 5:00 مساءً ، كتب Jim Moody [email protected] :

أنت محق SimenB https://github.com/simenb ، لقد غيرت شيئًا ما
في الاختبار الذي أجريته بين التبديل إلى Sinon الذي يبدو أنه قد مر.
عندما رجعت عن ذلك ، ما زال لا يعمل. أعتقد أنها ليست مشكلة
التي تم حلها.

-
أنت تتلقى هذا لأنه تم ذكرك.
قم بالرد على هذا البريد الإلكتروني مباشرة ، وقم بعرضه على GitHub
https://github.com/facebook/jest/issues/936#issuecomment-352488400 ، أو كتم الصوت
الخيط
https://github.com/notifications/unsubscribe-auth/AQRY9a5-s2_bjCWKNw5WiAJW-JeBf8W3ks5tBpoygaJpZM4IPGAH
.

-

دارين كريسويل
مطور عقود | ديفيلر المحدودة
البريد الإلكتروني: [email protected]
هاتف:
الموقع: http://www.develer.co.uk

يرجى الاخذ بالاعتبار مصلحة البيئة قبل طباعة هذا البريد الالكتروني، يرجى مراعاة بيئتك قبل طباعة بالبريد الالكتروني
تحذير: يمكن أن تنتقل فيروسات الكمبيوتر عبر البريد الإلكتروني. المستقبل
يجب التحقق من هذا البريد الإلكتروني وأي مرفقات بحثًا عن وجود فيروسات.
لا تتحمل شركة Develer Limited أي مسؤولية عن أي ضرر ناتج عن أي فيروس
المرسلة عن طريق هذا البريد الإلكتروني. لا يمكن ضمان نقل البريد الإلكتروني
آمنة أو خالية من الأخطاء حيث يمكن اعتراض المعلومات أو تلفها أو فقدها ،
دمرت أو وصلت متأخرة أو غير مكتملة أو تحتوي على فيروسات. المرسل
لذلك لا يتحمل المسؤولية عن أي أخطاء أو سهو في
محتويات هذه الرسالة ، التي تظهر نتيجة إرسال البريد الإلكتروني.

تحذير: على الرغم من أن شركة Develer Limited قد اتخذت احتياطات معقولة لـ
تأكد من عدم وجود فيروسات في هذا البريد الإلكتروني ، فلا يمكن للشركة قبولها
المسؤولية عن أي خسارة أو ضرر ينشأ عن استخدام هذا البريد الإلكتروني أو
المرفقات.

Develer Limited هي شركة محدودة مسجلة في إنجلترا وويلز. |
رقم تسجيل الشركة 09817616 | المكاتب المسجلة: SUITE 1 SECOND
FLOOR EVERDENE HOUSE، DEANSLEIGH ROAD، BOURNEMOUTH، المملكة المتحدة، BH7 7DU

شكرا ainesophaur على الحل.

في حالة ما إذا وجد أي شخص مثالًا عمليًا غير متزامن مفيدًا ، فإليك ما يلي:

//reportError.js
const functions = {};

functions._reportToServer = (error, auxData) => {
  // do some stuff
};

functions.report = (error, auxData = {}) => {
  if (shouldReportToServer()) {
    functions._reportToServer(error, auxData);
  }
};
export default functions;

// reportError.jest.js
import reportError from 'app/common/redux/lib/reportError';
reportError._reportToServer = jest.fn();

describe('test reportError', () => {
  it('reports ERROR to server as as error', () => {
   reportError.report(new Error('fml'), {});
    expect(reportError._reportToServer).toHaveBeenCalledTimes(1);
  });
});

@ jim-moody إذا فهمت المشكلة بشكل صحيح ، فيجب أن يعمل هذا كمثال:

const spyOnExampleFunc2 = jest.spyOn(example, 'func2');
example.func1();
expect(spyOnExampleFunc2).toBeCalled();

(يعمل هذا _ فقط_ إذا تم تصدير الوظائف على هيئة ثابتة ، كما في المثال الخاص بك)

dinvlad بطلي!

متابعة من صفحة كائن المزاح إلى صفحة وظائف Mock قد يكون تحسينًا لمستندات الدعابة عند الاستهزاء:

  • وظيفة jest.isMock (fn)
  • jest.genMockFromModule (moduleName)
  • jest.mock (اسم الوحدة ، المصنع ، الخيارات)
  • jest.unmock (moduleName)
  • jest.doMock (اسم الوحدة ، المصنع ، الخيارات)
  • jest.dontMock (moduleName)

حالة الاستخدام الخاصة بي هي أنه بصفتي مستخدمًا جديدًا لـ jest ، أقوم بترحيل بعض أكواد mocha + sinon.js إلى الدعابة. كان لدي بالفعل جواسيس وتوقعات لذلك اعتقدت أن الأمر سيكون سهلاً. ولكن بعد قراءة هذا الموضوع وقراءة مستندات الدعابة على وظائف Mock ، كان لدي انطباع بأن استخدام المزاح بهذه الطريقة قد يحتاج إلى إعادة كتابة لاختباراتي أو فهم مفصل لـ ESM أو Babel ... أو أي ارتباك آخر.

شكرًا لـ Jest - إنها تجعل اختباراتي أسهل في الكتابة / الفهم وأسرع في التنفيذ. :)

العلاقات العامة توضيح المستندات موضع ترحيب كبير! 🙂

للسخرية من وحدات معينة فقط باستخدام بنية الوحدة النمطية ES ، يمكنك استخدام يتطلب .requireActual لاستعادة الوحدات الأصلية ، ثم الكتابة فوق الوحدة التي تريد محاكاةها:

import { foo } from './example';

jest.mock('./example', () => (
  ...require.requireActual('./example'),
  foo: jest.fn()
));

test('foo should be a mock function', () => {
  expect(foo('mocked!')).toHaveBeenCalledWith('mocked!');
});

يبدو مقلوبًا ، لكنها أبسط طريقة صادفتها. نصيحة القبعة لـ joshjg.

لقد ضللت مكانًا ما في المناقشة الطويلة ، كان لدي سؤال للتو ، هل هناك أي طريقة لاختبار ما إذا كان يتم استدعاء التنفيذ الفعلي للوظيفة؟

بناءً على ما أفهمه ، إذا كنت بحاجة إلى استخدام jest.fn() ، فستتجاوز الوظيفة الأصلية ، ولكن إذا لم أستخدمها ، ستعطيني وحدة التحكم خطأ تقول إنها يجب أن تكون jest.fn() function or a spy

أحاول اختبار برمجية وسيطة حيث سيتم تمرير الطلب ، لذلك إذا سخرت منه ، فسيتم فقد كل المنطق ولن يتم تمرير البيانات إلى البرامج الوسيطة التالية. إذا لم أسخر من ذلك ، فهل يمكنني اختبار استدعاء هذه الوظيفة بأي حال من الأحوال؟

يمكنك استخدام jest.spyOn ، ربما؟ بشكل افتراضي ، تستدعي الوظيفة الأساسية

شكرًا على المساعدة ، لقد حاولت ، لكن الاختبار يشير إلى أنه لم يتم الاتصال بها مطلقًا على الرغم من استدعائها لأنني وضعت console.log وتم طباعته

ملف الاختبار

import errorHandler from '../../controller/errorHandler'

describe('auth test', () => {
  describe('test error: ', () => {
    const test1 = jest.spyOn(errorHandler, 'handleClientError')
    test('should return 400', (done) => {
      request(app)
      .post('/auth/error')
      .then((res) => {
        expect(res.statusCode).toBe(400)
        expect(test1).toBeCalled()
        done()
      })
    })

معالج الأخطاء

module.exports = {
  handleClientError () {
    console.log('err')
  }
}

وحدة التحكم

console.log src/controller/errorHandler.js:10
      err

  ● auth test › test error:  ›  should return 400

    expect(jest.fn()).toBeCalled()

    Expected mock function to have been called.

      18 |         expect(res.statusCode).toBe(400)
    > 19 |         expect(test1).toBeCalled()
      20 |         done()
      21 |       })
      22 |     })

هل الوظيفة تسمى handleClientError أو logError ؟

WangHansen من المثال الخاص بك يجب أن يكون الرمز الخاص بك expect(errorHandler.handleClientError).toBeCalled() // > true

WangHansen هل يمكنك إضافة .mockImplementation() إلى jest.spyOn() ؟ باعتباري شخصًا قادمًا من الياسمين ، فقد وجدت هذه النصيحة ضرورية لتحقيق نفس وظائف جواسيس الياسمين. على سبيل المثال

const mockModuleFunction = jest
  .spyOn(module, 'function')
  .mockImplementation(() => 'hello');
...
expect(mockModuleFunction.mock).toBeCalled();

إذا لم تكن _ لا تستخدم mockImplementation() ، فإن jest.spyOn() ينتج كائنًا _not_ mock (afaiu) وهو في الواقع ينسجم مع التنفيذ الأصلي. إذا كان عليك الاحتفاظ بالتنفيذ المحلي ، فربما يكون الأمر يستحق الاستخدام

const moduleFunction = module.function;
jest.spyOn(module, 'function').mockImplementation(moduleFunction);
...

لست متأكدًا من أن هذا ضروري ، ولكن تأكد من أنه _يجب_ أن يعمل.

العودة إلى الطلب الأصلي ...

ألا يمكنك فقط التفاف وكيل حول استيراد *؟ على سبيل المثال

استيراد * كاختبار من "./myfile.js" ؛

معالج const = {
/ ** الاعتراضات: الحصول على الخصائص * /
get (target، propKey، Receiver) {
console.log ( GET ${propKey} ) ؛
عودة 123 ؛
} ،

/** Intercepts: checking whether properties exist */
has(target, propKey) {
    console.log(`HAS ${propKey}`);
    return true;
}};

const p = وكيل جديد (اختبار) ؛

يوم الثلاثاء ، 30 يناير ، 2018 الساعة 4:24 مساءً ، Denis Loginov [email protected]
كتب:

WangHansen https://github.com/wanghansen هل يمكن أن تضيف
.mockImplementation () إلى jest.spyOn ()؟ كشخص قادم من
ياسمين ، لقد وجدت هذه النصيحة ضرورية لتحقيق نفس الوظائف مثل
جواسيس ياسمين. على سبيل المثال

const mockModuleFunction = jest
.spyOn (وحدة ، "وظيفة")
.mockImplementation (() => 'hello') ؛ ... توقع (mockModuleFunction.mock) .toBeCalled () ؛

إذا لم تستخدم mockImplementation () ، فإن jest.spyOn () ينتج ملف
كائن ليس زائفًا (afaiu) وهو في الواقع ينسجم مع المواطن الأصلي
تطبيق. إذا كان عليك الاحتفاظ بالتنفيذ المحلي ، فربما يكون كذلك
يستحق الاستخدام

const moduleFunction = module.function ؛ jest.spyOn (module، 'function'). mockImplementation (moduleFunction) ؛ ...

لست متأكدًا من أن هذا ضروري ، لكن متأكدًا تمامًا من أنه يجب أن يعمل.

-
أنت تتلقى هذا لأنه تم ذكرك.
قم بالرد على هذا البريد الإلكتروني مباشرة ، وقم بعرضه على GitHub
https://github.com/facebook/jest/issues/936#issuecomment-361648414 ، أو كتم الصوت
الخيط
https://github.com/notifications/unsubscribe-auth/AQRY9VXyHNYatwOOY6EV637WGQH9k5Plks5tP0I9gaJpZM4IPGAH
.

-

دارين كريسويل
مطور عقود | ديفيلر المحدودة
البريد الإلكتروني: [email protected]
هاتف:
الموقع: http://www.develer.co.uk

يرجى الاخذ بالاعتبار مصلحة البيئة قبل طباعة هذا البريد الالكتروني، يرجى مراعاة بيئتك قبل طباعة بالبريد الالكتروني
تحذير: يمكن أن تنتقل فيروسات الكمبيوتر عبر البريد الإلكتروني. المستقبل
يجب التحقق من هذا البريد الإلكتروني وأي مرفقات بحثًا عن وجود فيروسات.
لا تتحمل شركة Develer Limited أي مسؤولية عن أي ضرر ناتج عن أي فيروس
المرسلة عن طريق هذا البريد الإلكتروني. لا يمكن ضمان نقل البريد الإلكتروني
آمنة أو خالية من الأخطاء حيث يمكن اعتراض المعلومات أو تلفها أو فقدها ،
دمرت أو وصلت متأخرة أو غير مكتملة أو تحتوي على فيروسات. المرسل
لذلك لا يتحمل المسؤولية عن أي أخطاء أو سهو في
محتويات هذه الرسالة ، التي تظهر نتيجة إرسال البريد الإلكتروني.

تحذير: على الرغم من أن شركة Develer Limited قد اتخذت احتياطات معقولة لـ
تأكد من عدم وجود فيروسات في هذا البريد الإلكتروني ، فلا يمكن للشركة قبولها
المسؤولية عن أي خسارة أو ضرر ينشأ عن استخدام هذا البريد الإلكتروني أو
المرفقات.

Develer Limited هي شركة محدودة مسجلة في إنجلترا وويلز. |
رقم تسجيل الشركة 09817616 | المكاتب المسجلة: SUITE 1 SECOND
FLOOR EVERDENE HOUSE، DEANSLEIGH ROAD، BOURNEMOUTH، المملكة المتحدة، BH7 7DU

dinvladiampeterbanjoSimenB شكرا مرة أخرى لجميع مساعدتكم، ولكن unfortunnately، فإن أيا من الطرق التي قد اقترح عمل. أتساءل عما إذا كان ذلك بسبب استدعاء الوظيفة على شكل next(err) . المنطق هو أنه عند فشل الطلب ، سيتم تمريره إلى errorHandler عن طريق استدعاء return next(err) . بالتأكيد يتم استدعاء الوظيفة لأنني عندما أضفت console.log ، فإنها تطبع. لكن الاختبارات تشير إلى أنه لم يتم الاتصال به مطلقًا

dinvlad لقد جربت طريقتك ، لم تنجح ، لكن شكرًا على مساعدتك على أي حال. كنت أتساءل فقط لماذا تحتاج إلى الاتصال بـ mockImplementation ، وفقًا للوثيقة الرسمية في

WangHansen نعم ، أنت محق في أنه مطلوب فقط عندما يريد المرء تجاوز الطريقة الأصلية. كنت فقط أطرح فكرة لتلك المواقف.

أحد أسباب فشلها بالنسبة لك هو عدم التزامن. إذا كانت طريقتك تستخدم عمليات الاسترجاعات و / أو الوعود (أو غير متزامن / انتظار) ، فأنت بحاجة إلى التأكد من تنفيذ توقعاتك بالفعل قبل إنهاء طريقة الاختبار. هناك طريقة خاصة expect.assertions(N) لتأكيد ذلك. تأكد أيضًا من تنفيذ توقعاتك فقط بعد استدعاء الكود الموجود داخل عمليات الاسترجاعات / الوعود. أنا متأكد من أنك نظرت إلى ذلك ولكن للإشارة فقط ، https://facebook.github.io/jest/docs/en/asynchronous.html

من المؤسف أن الاستهزاء باستخدام fns داخليًا كما يصف seibelj غير ممكن بدون تغيير

في رأيي ، لا ينبغي أن تقود الاختبارات كيفية تنفيذ منطق الأعمال 😕 (على الأقل ليس إلى هذه الدرجة)

هل هناك أي خطط لتنفيذ مثل هذا السلوك من باب الدعابة؟

أهلا،
تأخرت Kinda في المناقشة بأكملها ، لكن قراءة المناقشة بأكملها - ما زلت لم أتمكن من القيام بذلك.
لم ينجح حل
هل تغير أي شيء منذ بداية هذه المناقشة؟ هل فاتني شيء؟

لقد قمت بتكييف حل greypants قليلاً وقد

import Module from './module'
import { getJSON } from './helpers'

jest.mock('./helpers', () =>
  Object.assign(require.requireActual('./helpers'), {
    getJSON: jest.fn()
  })
)

test('should use the mock', async () => {
  getJSON.mockResolvedValue('foo')
  await expect(module().someAsyncThingThatUsesGetJSON()).resolves.toEqual('foo')
})

test('should use the actual module', () => {
  expect(module().someFunctionThatUsesAHelper()).toBe(true)
})

لا يزال هذا يبدو نوعًا من الاختراق ولم تكن المستندات مفيدة. أنا ممتن لـ Jest والفريق الذي يقف وراءه ولكن يبدو أنه حالة استخدام شائعة يجب أن يكون لها على الأقل حل "رسمي".

Sarahdayan @ ومن يهمه الأمر -
انتهى بي الأمر باستخدام babel-plugin-Rewire.
استغرق الأمر مني بعض الوقت للعثور على هذا البرنامج المساعد ، ولكن "twas a حل متماسك بما يكفي لعدم الشعور بالاختراق.

في معظم الحالات ، لا نريد الاستهزاء بوظيفة واحدة أو أكثر من الوحدة النمطية. إذا كنت تستخدم نظام المزاح العالمي يمكنك تحقيق ذلك باستخدام genMockFromModule و requireActual . هذا هو المثال:

//  __mocks__/myModule.js
const moduleMock = jest.genMockFromModule('./../myModule');

// in most cases we don't want to mock `someFunction`
moduleMock.someFunction = jest.fn(
  (...args) => require.requireActual('./../myModule').someFunction(...args)
)

module.exports = moduleMock;

يسمح هذا الحل باستخدام mocks لوظائف أخرى من الوحدة ، باستخدام التنفيذ الأصلي someFunction عندما يتم السخرية من الوحدة بأكملها ويسمح أيضًا بالسخرية من الوظيفة someFunction باستخدام mockImplementationOnce أو mockImplementation API.

لقد قرأت جميع المحادثات أعلاه ، ولكن لا يوجد حل واحد يناسبني.
إذا كنت لا تزال تبحث عن حل لحالة الاختبار هذه ، فإن الإجابة هي babel-plugin-Rewire ، فإن هذا المكون الإضافي يهدف إلى حل حالة السيناريو التي ناقشناها.
يرجى إلقاء نظرة على هذا lib ، وسوف تشكرني مرة أخرى لاحقًا.

لتلخيص الموضوع بالكامل أعلاه:

  1. لنفترض أن لديك وحدة m بوظائف f ، g و h حيث g و h call f . نود أن نسخر من f حتى يقوم g و h باستدعاء المحاكاة بدلاً من f الحقيقي. لسوء الحظ ، لا يمكن القيام بذلك بشكل مباشر ما لم يتم استدعاء f دائمًا من خلال exports كما وصف cpojer . هذا مستحيل إذا كانت الوحدة النمطية الخاصة بك تستخدم بناء جملة استيراد / تصدير ES6 في TypeScript (وأعتقد أن الأمر نفسه ينطبق في Babel).
  2. ومع ذلك ، لنفترض أننا نقلنا f إلى وحدة نمطية أخرى m2 . ثم m سيكون له كشف مثل import {f} from 'm2' وعندما g و h f ، فإنهم في الواقع يتصلون بـ m2.f حيث m2 = require('./m2') (هكذا ستبدو ترجمة Babel / TypeScript). هذا يجعل من الممكن السخرية من f بشكل موثوق كما وصفه greypants . بمعنى آخر ، لا يمكنك محاكاة المكالمات بشكل موثوق إلا إذا تجاوزت حدود الوحدة . ملاحظة: ينتج حل greypants الآن رسالة الخطأ هذه: "مصنع الوحدة jest.mock() غير مسموح له بالإشارة إلى أي متغيرات خارج النطاق - وصول متغير غير صالح: __assign". أظن أن هذا خطأ في Jest ؛ كحل بديل ، استخدم Object.assign كما هو موضح أدناه.
  3. ولكن إذا أردت ، بدلاً من الاستهزاء بوظيفة أو وظيفتين ، الاستهزاء بكل شيء باستثناء وظيفة أو وظيفتين ، فاستخدم رمزًا مثل داركوويك .

مثال على (2):

~~~ شبيبة
// وحدة m.js
استيراد {f} من "./m2"
دالة التصدير g () {return 'f return' + f ()؛ } ؛

// وحدة m2.js
دالة التصدير f () {return 'the real f'؛ }

// test.js
استيراد * كـ م من "./m"

jest.mock ('./ m2'، () => تعيين الكائن (
need.requireActual ('./ m2') ، {
f: jest.fn (). mockReturnValue ('MOCK')
})) ؛

test ('mocked'، () => {
توقع (mg ()). toEqual ('f عاد MOCK') ؛
}) ؛
~~~

أثناء اختبار هذا ، واجهت الرقم 2649: استدعاء jest.mock داخل الاختبار ليس له أي تأثير ، وإذا كنت تسميه على النطاق العالمي ، فلا يمكنك unmock قبل الاختبارات الأخرى. مزعج جدا.

شكرا!! تضمين التغريدة
كنت أبحث عن هذا لفترة من الوقت

في حالة عدم توفر المستندات ، نرحب دائمًا بموظفي العلاقات العامة لتوضيحها 🙂

مرحبا جميعا!

لقد لعبت قليلاً وكانت لدي الفكرة التالية لحل هذه المشكلة:

  • محاكاة وحدة نمطية ، ولكن الوحدة النمطية تم الاستهزاء بها في سلسلة النموذج الأولي.
  • توفير طريقة لإضافة خصائص إلى الوحدة النمطية التي تم الاستهزاء بها (والتي ستتجاوز الخصائص من النموذج الأولي)
  • قدم أيضًا طريقة لإزالة الخصائص من الوحدة النمطية التي تم الاستهزاء بها (لاستخدام الخاصية من النموذج الأولي مرة أخرى).
// m1.js
export const f = () => "original f"

// __mocks__/m1.js
const originalM1 = require.requireActual("../m1");
// set the original as a prototype
let mockModule: any = Object.create(originalM1);
const __setMock = (name, value) => {
  mockModule[name] = value;
};
const __removeMock = (name) => {
  Reflect.deleteProperty(mockModule, name);
};
// enhance the mocked module to allow overriding original exports
module.exports = Object.assign(mockModule, { __setMock, __removeMock });


// m1.test.js
import { f } from "./m1";

jest.mock("./m1");

test("mocking stuff", () => {
  // here nothing is mocked - the original module is used
  expect(f()).toBe("original f");

  // override the export f of the module
  require("./m1").__setMock("f", () => "mocked f");
  expect(f()).toBe("mocked f");

  // set it back to the original
  require("./m1").__removeMock("f");
  expect(f()).toBe("original f");

  //override with another value
  require("./m1").__setMock("f", () => "another mocked f");
  expect(f()).toBe("another mocked f");
});

2 سنتي:

لقد اختبرت الكثير من الحلول (إن لم يكن جميعها) والحل الوحيد الذي نجح معي هو هذا (jest 23):

// importedModule.js
export const funcB = () => {
  return "not mocked";
};
export const funcA = () => {
  return mockProxy.funcB(); // this works but it's soooo hacky
};

export const mockProxy = {
  funcB
};

// moduleToTest.js
import { funcA } from "./moduleImported.js";

export default function() {
  return funcA();
}

// test
let moduleImported = require("./moduleImported.js");
moduleImported.mockProxy.funcB = jest.fn(() => "mocked");
const funcToTest = require("./moduleToTest.js").default; // or import

it("should return mocked", function() {
  expect(funcToTest()).toBe("mocked");
});

توصلت إلى استنتاج مفاده أنه ليس من الجيد محاولة القيام بذلك للأسباب التالية:

  • يعرف test.js الكثير عن تفاصيل تنفيذ importedModule.js
  • الحل هو الهش ولن يفهم أحد الغرض من mockProxy خلال النظر إلى importedModule.js

هل وجد أي شخص حلا لهذا يعمل؟

أنا استخدم:

"jest": "^21.2.1",
"jest-cli": "^21.2.1",

jamesone هل قرأت هذا https://github.com/facebook/jest/issues/936#issuecomment -410080252؟

لقد نجح هذا بالنسبة لي ، بناءً على إجابة

في حالتي ، أردت السخرية من تبعية في node_modules ، دعنا نسميها "مكونات مشتركة". يقوم بتصدير عدد من المكونات المسماة. أردت أن أسخر من اثنين فقط من هذه الصادرات المسماة ، وأترك ​​الباقي على أنه شيء حقيقي.

لذلك في __mocks__/shared-components.js لدي:

const original = require.requireActual('shared-components');

module.exports = {
...original,
moduleNameToOverride: jest.fn().mockImplementation(() => {
      return 'whatever';
    }),
}

في حالتي كنت أقوم بإيقاف التطبيقات. نأمل أن يساعد هذا شخصًا ما في المستقبل.

لقد واجهت نفس المشكلة مؤخرًا ، ساعدتني المحادثة في هذا الموضوع على فهم أفضل وقمت بتلخيص النتائج التي توصلت إليها هنا https://medium.com/@DavideRama/mock -spy-exported-function-within-a-single- وحدة في jest- cdf2b61af642

مستوحى من حل qwertie

mockGet = jest.fn()
jest.mock('my-module', () => ({
  ...jest.requireActual('my-module'),
  get: mockGet
}))

Major breakfast لا تعمل مع React.lazy ؟

const mockLazy = jest.fn();

jest.mock('React', () => ({
    ...jest.requireActual('React'),
    lazy: mockLazy
}));

ما زلت أحصل على ReferenceError: React is not defined .

للسخرية من وحدة دالة تصدير مستقلة:
تصدير جميع الوظائف الفردية جزء من التصدير الافتراضي من الملف.

مثال:
dataDao.js

وظيفة getData ()
وظيفة setData ()
وظيفة deleteData ()
تصدير {getData، setData، deleteData}

يمكنك الآن استيراد جميع الوظائف من الملف إلى اختبار jest الخاص بك عن طريق التسمية الافتراضية ؛

dataDao.spec.js

استيراد * كـ dataDao من "../dataDao" ؛
// تجسس على الوحدات التي تشير إلى الاسم الافتراضي المعين عند الاستيراد
jest.spyOn (dataDao، 'getData')
jest.spyOn (dataDao، 'setData')
jest.spyOn (dataDao، 'deleteData')

vchinthakunta ، قد ينجح ذلك ، ولكن يبدو أنه انتهاك لهدف رئيسي من بناء جملة التصدير / الاستيراد: لن تتمكن الوحدات النمطية الأخرى من استيراد طرق أو حقول بيانات محددة عبر

import { justThisThing } from 'someModule';

هل أفتقد شيئًا هناك؟

Major breakfast لا تعمل مع React.lazy ؟

const mockLazy = jest.fn();

jest.mock('React', () => ({
    ...jest.requireActual('React'),
    lazy: mockLazy
}));

ما زلت أحصل على ReferenceError: React is not defined .

أعتقد أن "React" يجب أن تكون صغيرة هنا لأنها تشير إلى الاستيراد؟

jest.mock('react'...)

لقد حصلت على الكود الخاص بي يعمل باستخدام ما يلي وهو أبسط من الحلول الأخرى التي رأيتها هنا. لا يتطلب منك استخدام require أو إعداد صادرات default .

المساعدون / navigation.js

export const initHeader = () => {
    // initialise header
    ...
}

...

export const anotherHelperFunction = () => {
    // do stuff
    ...
}

المكون الذي يستخدم navigation.js

import { initHeader } from '../helpers/navigation';

jest.mock('../helpers/navigation');

...

describe('Test component', () => {

    it('should reinitialise header', () => {
        const mockHeaderInit = jest.fn();
        initHeader.mockImplementation(mockHeaderInit);

        const component = mountComponent(mockProps);
        component.simulate('click');

        expect(mockHeaderInit).toBeCalled();
    }
}
mockGet = jest.fn()
jest.mock('my-module', () => ({
  ...jest.requireActual('my-module'),
  get: mockGet
}))
ReferenceError: mockGet is not defined

       4 | const mockGet = jest.fn();
       5 | jest.mock('./browserStorage', () => ({
       6 |   ...jest.requireActual('./browserStorage'),
    >  7 |   get: mockGet,
         |        ^
       8 | }));

jest.mock ، استخدم doMock أو

jest.mock('./browserStorage', () => ({
  ...jest.requireActual('./browserStorage'),
  get: jest.fn(),
}));

const {get: mockGet} = require('./browserStorage');

هذا يبدو وكأنه مشكلة شائعة بما فيه الكفاية. هل هناك شيء بخصوص هذا في المستندات الدعابة؟

إذن ، هل هناك حل للاستهزاء بوظيفة داخل نفس الوحدة ؟

نجحت الطريقة التالية بالنسبة لي ، تتمثل الحيلة في إعادة تعيين الوظيفة التي تم الاستهزاء بها مرة أخرى في نهاية الاختبار.
هذا المثال يسخر من وظيفة التحقق من الوحدة النمطية jsonwebtoken.

  test('perform my test', async () => {
    // save the real jwt.verify function
    const verify = jwt.verify
    // mock it.
    jwt.verify = jest.fn().mockReturnValue({ sub: 0 })
    // do the test
    ...
    // set the real function back.
    jwt.verify = verify
  })

نجحت الطريقة التالية بالنسبة لي ، تتمثل الحيلة في إعادة تعيين الوظيفة التي تم الاستهزاء بها مرة أخرى في نهاية الاختبار.
هذا المثال يسخر من وظيفة التحقق من الوحدة النمطية jsonwebtoken.

  test('perform my test', async () => {
    // save the real jwt.verify function
    const verify = jwt.verify
    // mock it.
    jwt.verify = jest.fn().mockReturnValue({ sub: 0 })
    // do the test
    ...
    // set the real function back.
    jwt.verify = verify
  })

أين تستخدم const verify ؟ على أي حال ، لن يعمل هذا إلا إذا لم تكن الوظيفة التي تم الاستهزاء بها دالة ثابتة مُصدرة

إذن ، هل هناك حل للاستهزاء بوظيفة _ ضمن نفس الوحدة_؟

بعد إجراء الكثير من البحث ، يتمثل الحل لهذه المشكلة في تخزين صادراتك في كائن واحد يمكن أن تشير إليه وظيفتك والسحر. جاءت جميع المواد أدناه إلى نفس الإجماع.

https://github.com/facebook/jest/issues/936#issuecomment -438975674
https://medium.com/@qjli/how -to-mock-specific-module-function-in-jest-715e39a391f4
https://luetkemj.github.io/170421/mocking-modules-in-jest

من المدهش بالنسبة لي أنه لم يذكر أحد حلًا مختلفًا ، إذا كان قابلاً للتطبيق بالنسبة إلى التعليمات البرمجية الخاصة بك ، فإنه يلغي تمامًا كل هذه المشكلات ويجعل اختبار الكود المطلوب أمرًا بسيطًا للغاية:

_ انقل الوظيفة الفردية التي تريد محاكاةها إلى الوحدة النمطية الخاصة بها.

عنجد. إذا تمت كتابة الوحدة النمطية الخاصة بك بحيث تحتاج إلى اختبار الأجزاء الداخلية منها بشكل منفصل عن الأجزاء الداخلية الأخرى ، فمن المؤكد تقريبًا أن فصلك ينتهك مبدأ المسؤولية الفردية (نعم ، إنه ليس فصلًا حقيقيًا ، ولكن الوحدة تعمل مثل الفصل ، الوحدات النمطية كونها حاوية وحدة من التعليمات البرمجية). تقسيم هذا المصاص ، والازدهار ، يمكنك السخرية من المطلوب.

إذا كانت الوظيفة التي تم الاستهزاء بها تعتمد على مجموعة من الحالات الخاصة ، فلا يزال هذا ليس سببًا جيدًا لعدم تقسيم الوحدة النمطية الخاصة بك بطريقة أو بأخرى. حقيقة أنه يعتمد على مجموعة من الحالات الداخلية تعني ، بالنسبة لي ، أن اهتمامات الوحدة لم يتم التفكير فيها بوضوح. ربما هناك وحدة نمطية ثالثة لتقسيمها ، والتي تمثل نوعًا من فئة البيانات أو DTO ، والتي يمكن تمريرها كوسيطة.

أيضًا ، هل تقوم بتصدير وظائف للاختبار فقط والتي ستكون خاصة بخلاف ذلك؟ لماذا تستدعي الشفرة الخارجية الوظيفة التي تم الاستهزاء بها مباشرة ، ولكنها تستدعي أيضًا الوظائف الأخرى التي يحتاجونها هم أنفسهم لاستدعائها؟ أراهن أن هناك نوعًا من قطع الاتصال يحدث هنا. ربما تحتاج الوظيفة التي تم الاستهزاء بها إلى البقاء ولكن يجب تقسيم جميع الوظائف إلى نصفين مع إزالة النصف إلى وحدة أخرى. انت وجدت الفكرة.

عندما يصبح الاختبار صعبًا للغاية ، فإنه دائمًا ما يكون علامة على الحاجة إلى إعادة البناء ...

لا يتطلب اختبار معدات الصيد:

const tomfoolery = require('tomfoolery'); // no longer required

بعد قراءة هذا الموضوع واختبار الحلول المقترحة ، ما زلت لا أستطيع تشغيل هذا. من خلال ما قرأته ، قام بعض الأشخاص بهذا العمل ولكن لا يمكنني معرفة كيفية القيام بذلك.

هل يمكن أن يخبرني أحدهم بالرمز الذي أحتاج إلى إضافته إلى المثال التالي حتى تنجح الاختبارات؟

// a.js
export const foo = () => 'foo-' + bar()
export const bar = () => 'bar'
// a.test.js
import {
  foo,
  bar
} from './a'

describe('foo', () => {
  it('should return foo-MOCKED_BAR', () => {
    expect(foo()).toBe('foo-MOCKED_BAR')
  })

  it('should have the mocked implementation of bar', () => {
    expect(bar()).toBe('MOCKED_BAR')
  })
})

describe('bar', () => {
  it('should have the original implementation of bar', () => {
    expect(bar()).toBe('bar')
  })
})

describe('foo and bar together', () => {
  it('should have the original implementation of both', () => {
    expect(foo()).toBe('foo-bar')
  })
})

شكرا!

أردت فقط أن أسخر من طريقة لوداش واحدة مثل لوداش عشوائي وتمكنت من فعل ذلك بسهولة باستخدام:

module.js

const lodash = require('lodash');

module.exports = function() {
  return lodash.random();
}

test.js

const lodash = require('lodash');
const module = require('./module.js);

it('mocks lodash', () => {
    jest.spyOn(lodash, 'random').mockImplementationOnce(() => {
      return 2;
    });

    expect(module()).toEqual(2)
});

امل ان يساعد :)

كان الشيء الذي نجح فريقنا في العمل مع الكتابة المطبوعة هو إنشاء const نقوم بتصديره بدلاً من تصدير الوظيفة مباشرةً.
لا يعمل:
export function doSomething(a, b) {}
عمل:
export const doSomething = function (a, b) {}

لقد غيرت التصدير مثل ما يفعله arbielsk ، إنه يعمل! لكني لا أعرف ما هو الفرق بين نوعين من التصدير ...

dgrcode هل سبق لك أن وجدت حلاً أعتقد أن الاستهزاء فقط هو في الأساس إعادة توصيل أسلاك الواردات بحيث ترى المشاهدات الخارجية للوحدة الأساليب التي تم الاستهزاء بها. ومع ذلك ، في المثال الخاص بك ، foo و bar في نفس الوحدة ، لذا لا يمكن الاستهزاء بنظرة foo لـ bar .

أعتقد أن خياراتك إما:
1) أعد تنظيم الكود الخاص بك بحيث يقوم foo باستيراد وحدة تتضمن bar
2) استخدم برنامج babel-plugin-Rewire

من فضلك صححني إذا كنت أسيء الفهم!

كان لدي مطلب مختلف قليلاً: أردت أن أسخر من وحدة كاملة _استثناء_ لوظيفة واحدة. باستخدام حل Major breakfast كنقطة انطلاق ، توصلت إلى ما يلي:

jest.mock('my-module', () => ({
  ...jest.genMockFromModule('my-module'),
  myFunction: jest.requireActual('my-module').myFunction
}))

dgrcode هل سبق لك أن وجدت حلاً foo و bar في نفس الوحدة ، لذا لا يمكن الاستهزاء بنظرة foo لـ bar .

أعتقد أن خياراتك إما:

  1. أعد تنظيم الكود الخاص بك بحيث يقوم foo باستيراد وحدة تتضمن bar
  2. استخدم برنامج babel-plugin-Rewire

من فضلك صححني إذا كنت أسيء الفهم!

كان هذا في الواقع حالتي
كان فهمي لكيفية السخرية من الوحدات معطلاً 🤦‍♂

في الأساس
إذا كانت الوظيفة 2 في نفس الوحدة ، وتتصل ببعضها البعض

إذا كان foo يتصل بـ bar

function bar() {
  return 'some-result'
}

function foo(){
  return bar()  // <-- the function I want to mock 
}

ضع bar في ملف جديد (للسخرية)

لقد قمت بنقل طريقة bar في ملف جديد ، والآن يمكنني استخدام العديد من الأمثلة أعلاه
شكراً جزيلاً لـ @ yoni-abtech في جعلني أفهم ذلك 🤣

بالطريقة التي أراها بعد قراءة هذا الموضوع بالكامل واختباره مرارًا وتكرارًا ، هناك 3 خيارات ....

الخيار 1 - التصريح عن جميع الوظائف باستخدام const

هذا يتطلب منك تفويض استخدام تعبيرات الوظيفة على الإعلانات . لحسن الحظ ، فإن قاعدة eslint func-style تدعمك.

باستخدام export const يسمح لك spyOn الوظائف التي يتم استخدامها من قبل وظائف أخرى من داخل وحدة _same_.

// hello.js
export const message = () => {
  return 'Hello world';
}

export const foo = () => {
  return message();
}
// hello.test.js
import * as testModule from './hello.js';

describe('test spyon with function expressions', function () {
  afterAll(() => {
    jest.restoreAllMocks();
  });
  it('should NOT mock message in foo', function () {
    const actual = testModule.foo();

    expect(actual).toBe('Hello world');
  });

  it('should mock message in foo', function () {
    jest.spyOn(testModule, 'message').mockReturnValue('my message');

    const actual = testModule.foo();

    expect(actual).toBe('my message');
    expect(testModule.message).toHaveBeenCalledTimes(1);
  });
});

الخيار 2 - استخدم المكون الإضافي بابل rewire

إذا كنت لا ترغب في استخدام تعبيرات الوظائف (على سبيل المثال ، استخدام const ) فقد يكون هذا أسلوبًا جيدًا.

هذا يسمح لك بوظائف _rewire_ (الملقب مستندات يبدو أنك يمكن تركيب شبكة كهرباء جديدة وظائف في نفس الوحدة التي لم يتم حتى تصديرها من وحدة 👍، تخيل المثال التالي دون تصدير message ظيفة.

مثال:

// hello.js
export function message() {
  return 'Hello world';
}

export function foo() {
  return message();
}
// hello.test.js
import * as testModule from './hello.js';

describe('test rewire api', function() {
  it('should NOT mock message in foo', function () {
    const actual = testModule.foo();

    expect(actual).toBe('Hello world');
  });

  it('should mock message in foo', function () {
    testModule.__RewireAPI__.__Rewire__('message', jest.fn().mockReturnValue('my message'));

    const actual = testModule.foo();

    expect(actual).toBe('my message');
    expect(testModule.message).toHaveBeenCalledTimes(1);
    testModule.__RewireAPI__.__ResetDependency__('message');
  });
});

انظر المستندات للحصول على أمثلة.

ملاحظة: يتطلب ترشيح بابل

الخيار 3 - افصل جميع الوظائف إلى وحدات / ملفات منفصلة

هذا الخيار هو الأقل ملاءمة ولكنه سيعمل بشكل جيد مع الوظيفة النموذجية mock .


ملاحظة: على الرغم من أن هذا المداس كان مفيدًا للغاية وممتعًا في كثير من الأحيان ، إلا أنني آمل أن يخفف هذا الملخص من حاجة الآخرين لقراءة الموضوع بأكمله. ✌️

شكرًا nickofthyme ، لقد أنهيت للتو يومين من ضرب رأسي ضد هذا.

nickofthyme فشل option 1 في كل من تطبيقي ، و create react app :

 FAIL  src/hello.test.js
  test spyon with function expressions
    ✓ should NOT mock message in foo (3ms)
    ✕ should mock message in foo (6ms)

  ● test spyon with function expressions › should mock message in foo

    expect(received).toBe(expected) // Object.is equality

    Expected: "my message"
    Received: "Hello world"

      17 |     const actual = testModule.foo();
      18 |
    > 19 |     expect(actual).toBe("my message");
         |                    ^
      20 |     expect(testModule.message).toHaveBeenCalledTimes(1);
      21 |   });
      22 | });

      at Object.toBe (src/hello.test.js:19:20)

Test Suites: 1 failed, 1 total
Tests:       1 failed, 1 passed, 2 total
Snapshots:   0 total
Time:        1.848s
Ran all test suites related to changed files.

danielhusar يبدو أنك على صواب. آسف ، كان يجب أن أجرب هذا مع CRA.

حصلت على السخرية الموصوفة في الخيار 1 للعمل هنا . اختبره باستخدام البرنامج النصي yarn test:hello .

نتيجة

> yarn test:hello
yarn run v1.16.0
$ jest --config=jest.config.js -t=hello --verbose
 PASS  src/hello/hello.test.ts
  test hello
    ✓ should NOT mock message in foo (3ms)
    ✓ should mock message in foo (1ms)

Test Suites: 1 skipped, 1 passed, 1 of 2 total
Tests:       1 skipped, 2 passed, 3 total
Snapshots:   0 total
Time:        1.392s
Ran all test suites with tests matching "hello".
✨  Done in 2.36s.

يتطلب استخدام ملف jest.config.js مخصص باستخدام ts-jest واستدعاء jest --config=./jest.config.js مباشرة ، وليس من خلال react-scripts . لست متأكدًا من كيفية تكوين jest في react-scripts ولكن أعتقد أنه قد تكون هناك طريقة لتحديث التكوين بطريقة ما.

يزيل هذا الإصلاح التحويلات لملفات *.css و *.svg ، لذا تجاهل أخطاء App.tsx .

هل هناك أي شيء محدد يجب القيام به لإنجاحه؟
أود أن أقول إن لدي إعدادًا قياسيًا جدًا (بدون ts) ولا يعمل خارج الصندوق.

سأنظر إليها أكثر قليلاً الليلة وأرى ما إذا كان ذلك ممكنًا.

danielhusar لقد بحثت في الليلة الماضية ولم أستطع الحصول على حل خارج الصندوق. المفتاح هو تكوين الدعابة transformer الذي يسمح لك CRA بتجاوزه في package.json#jest . وtranspiled الملفات شبيبة والخبر باستخدام babel-jest ولكن react-scripts كتل لك من استخدام .babelrc ملف التكوين ووضع test الحياة الفطرية التي وضعوا في react-scripts test هنا .

أتمنى أن أحفر أعمق ولكن ليس لدي الوقت الآن.

حسنًا ، ما زلت أكافح قليلاً لإنجاحه (على الإعداد المخصص ، وليس على cra).
(أحدث إصدار من كل من jest و babel-jest)

هذا هو التكوين الدعائي الخاص بي:

module.exports = {
  name: 'my-app',
  testURL: 'http://localhost/',
  setupFiles: ['<rootDir>/setup-jest.js'],
  setupFilesAfterEnv: ['<rootDir>/setup-test.js'],
  testMatch: ['**/__tests__/**/*.test.js?(x)', '**/?(*.)+(spec|test).js?(x)'],
  testEnvironment: 'jest-environment-jsdom-fifteen',
  snapshotSerializers: ['enzyme-to-json/serializer'],
  globals: {
    ENV: 'test',
  },
  transform: {
    '^.+\\.[t|j]sx?$': 'babel-jest',
  },
};

و babelrc الخاص بي:

{
  "presets": [
    "@babel/react",
    ["@babel/env", {
      "corejs": "3",
      "useBuiltIns": "entry",
      "loose": true,
      "exclude": [
        "es.string.split"
      ]
    }],
    "@babel/flow"
  ],
  "plugins": [
    "array-includes",
    "lodash",
    "@babel/plugin-syntax-dynamic-import",
    "@babel/plugin-syntax-class-properties",
    "@babel/plugin-proposal-class-properties",
    "@babel/plugin-proposal-object-rest-spread",
    "@babel/plugin-proposal-optional-chaining"
  ],
  "env": {
    "test": {
      "plugins": ["dynamic-import-node"]
    }
  }
}

لأي شخص يعاني مع الخيار 1 ، من المهم استخدام () => { return expression } بدلاً من () => (expression) الوظيفة.

حصلت على الخيار 1 للعمل عن طريق تعديله إلى:

import * as test from './test';

export const message = () => {
    return 'Hello world';
  }

  export const foo = () => {
    return test.message();
  }

ليست جميلة لكنها يجب أن تعمل.

nickofthyme ، الخيار 1 صحيح كما هو موجود لديك. ولكن إذا قمت بتغيير الرمز إلى:

const foo = () => {}
export { foo }

ثم ينكسر. من المفترض أنك تنشئ كائنًا جديدًا حرفيًا وتقوم بتصديره.

ملاحظة مثيرة للاهتمام. شكرا maletor

لدى Jest مثال بسيط جدًا وواضح في توثيقهم لكيفية السخرية من وحدة ما جزئيًا. يعمل هذا مع كل من عبارات ES import و Node request.
https://jestjs.io/docs/en/jest-object#jestrequireactualmodulename

Johncmunson هذه نقطة جيدة. ومع ذلك ، فإن هذا المثال الذي عرضته عن السخرية من وحدة لا يعمل إلا إذا كنت تحتاج فقط إلى تشغيل jest.mock _ مرة واحدة ولا تستخدم أي طريقة تم الاستهزاء بها تصديرًا آخر من تلك الوحدة.

خذ المثال أعلاه ... لقد أضفت bar لإظهار كيف أريد أن أسخر من الوحدة بشكل مختلف بين foo و bar .

export const message = (): string => {
  return 'Hello world';
}

export const foo = (): string => {
  return message();
}

export const bar = (): (() => string) => {
  return foo;
}

باستخدام jest.mock مع jest.requireActual أعتقد أنه سيجري شيئًا كهذا.

import * as mockTestModule from './hello';

jest.mock('./hello');
const actualTestModule = jest.requireActual('./hello');

describe('test hello', function () {
  afterAll(() => {
    jest.restoreAllMocks();
  });

  // first test doesn't depend on any other method of the module so no mocks
  it('should NOT mock message in foo', function () {
    const actual = actualTestModule.foo();

    expect(actual).toBe('Hello world');
  });

  // the second I want to mock message in foo
  it('should mock message in foo', function () {
    jest.spyOn(mockTestModule, 'message').mockReturnValue('my message');
    const actual = actualTestModule.foo();

    expect(actual).toBe('my message'); // fails
    expect(mockTestModule.message).toHaveBeenCalledTimes(1); // never called
  });

  it('should mock foo in bar', function () {
    jest.spyOn(mockTestModule, 'foo').mockReturnValue('my message');
    const actual = actualTestModule.bar();

    expect(actual()).toBe('my message'); // fails
    expect(mockTestModule.message).toHaveBeenCalledTimes(1); // never called
  });
});

حتى أنني حاولت السخرية منهم بشكل منفصل jest.doMock وما زلت أحصل على نفس النتيجة.


انقر لرؤية الرمز

""
استيراد * كوحدة اختبار من "./hello" ؛

وصف ("اختبار مرحبًا" ، الوظيفة () {
afterAll (() => {
jest.restoreAllMocks () ،
}) ؛

it('should NOT mock message in foo', function () {
  const actual = testModule.foo();

  expect(actual).toBe('Hello world');
});

it('should mock message in foo', function () {
  jest.doMock('./hello', () => {
    // Require the original module to not be mocked...
    const originalModule = jest.requireActual('./hello');

    return {
      ...originalModule,
      message: jest.fn().mockReturnValue('my message'),
    };
  });
  const actual = testModule.foo();

  expect(actual).toBe('my message'); // fails
  expect(testModule.message).toHaveBeenCalledTimes(1); // never called
});

it('should mock foo in bar', function () {
  jest.doMock('./hello', () => {
    // Require the original module to not be mocked...
    const originalModule = jest.requireActual('./hello');

    return {
      ...originalModule,
      foo: jest.fn().mockReturnValue('my message'),
    };
  });
  const actual = testModule.bar()();

  expect(actual).toBe('my message'); // fails
  expect(testModule.foo).toHaveBeenCalledTimes(1); // never called
});

}) ؛
""

تكمن المشكلة في هذا الأسلوب في أن طلب الوحدة الفعلية ، ثم استدعاء foo ، لا يزال يستدعي الوظيفة message الفعلية وليس الوهمي.

كنت أتمنى أن يكون الأمر بهذه البساطة ولكن مما أراه لا يساعد في الأمثلة من هذا الموضوع. إذا فقدت شيئًا ما هنا ، فيرجى إبلاغي بذلك. سأعترف بكل سرور بالخطأ.

بالنسبة لأي شخص يصادف هذا البحث عن حل ، يبدو أن ما يلي يعمل بالنسبة لي عند تصدير العديد من الدوال / const في ملف واحد ، واستيرادها في ملف أقوم باختباره

function mockFunctions() {
  const original = require.requireActual('../myModule');
  return {
    ...original, //Pass down all the exported objects
    test: jest.fn(() => {console.log('I didnt call the original')}),
    someFnIWantToCurry: {console.log('I will curry the original') return jest.fn((...args) => original.someFnIWantToCurry(...args)}),
  }
jest.mock('../myModule', () => mockFunctions());
const storage = require.requireMock('../myModule');
`

يعمل ما يلي وهو أقصر قليلاً:

const module = require('./module');
jest.spyOn(module, 'myFn').mockImplementation(() => 'val');

في Typescript ، فقط import بدلاً من require :

import * as module from './module';

هذا له ميزة جعل الحياة سهلة لاستعادة الوظائف الأصلية والسخرية الواضحة.

بالنسبة لأي شخص يصادف هذا البحث عن حل ، يبدو أن ما يلي يعمل بالنسبة لي عند تصدير العديد من الدوال / const في ملف واحد ، واستيرادها في ملف أقوم باختباره

function mockFunctions() {
  const original = require.requireActual('../myModule');
  return {
    ...original, //Pass down all the exported objects
    test: jest.fn(() => {console.log('I didnt call the original')}),
    someFnIWantToCurry: {console.log('I will curry the original') return jest.fn((...args) => original.someFnIWantToCurry(...args)}),
  }
jest.mock('../myModule', () => mockFunctions());
const storage = require.requireMock('../myModule');
`

يعمل ما يلي وهو أقصر قليلاً:

const module = require('./module');
jest.spyOn(module, 'myFn').mockImplementation(() => 'val');

في Typescript ، فقط import بدلاً من require :

import * as module from './module';

هذا له ميزة جعل الحياة سهلة لاستعادة الوظائف الأصلية والسخرية الواضحة.

أوه ، نعم أيضًا هذه الطريقة لا تعمل إذا كان الكائن الخاص بك يحتوي فقط على getter معرّف. يمكن أن تكون رسالة الخطأ كما يلي:

Test suite failed to run

    TypeError: Cannot set property useContent of #<Object> which has only a getter

ربما تحتاج إلى استخدام jest.mock(..) لهذه الحالة. : bowing_man:

تعمل أجهزتي باستخدام ما يلي:

import { unsubscribe } from "../lib/my-lib"
import { MyComponent } from "./index"

test("unsubscribe gets called", () => {
    const module = require("../lib/my-lib")
    jest.spyOn(
        module,
        "unsubscribe"
    ).mockImplementation(() => jest.fn())

    const { getByTestId } = render(() => <MyComponent  />)

    let button = getByTestId("trigger.some.action.button")

    fireEvent.press(button)

    expect(unsubscribe).toHaveBeenCalled()
})

لا يبدو أنيقًا جدًا ولا يتسع بسهولة ، فهو يعمل جيدًا على الرغم من أنه يناسب حالاتي في الوقت الحالي .. ولكن إذا كان بإمكان أي شخص اقتراح أي بنية أخرى فسيكون ذلك رائعًا! هذه هي الصيغة الوحيدة التي يبدو أنها تعمل.

كود وحدة es6:

export const funcA = () => {};
export const funcB = () => {
  funcA();
};

بعد تحويلها إلى CommonJS:

"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.funcB = exports.funcA = void 0;

var funcA = function funcA() {};

exports.funcA = funcA; // You can mock or add a spy  on this `funcA`

var funcB = function funcB() {
  funcA();  // This is still original `funcA`
};

exports.funcB = funcB;

هناك طرق عديدة لحل هذا الوضع.

  1. تحتاج إلى تغيير الكود مثل هذا ، حتى تتمكن من استخدام funcA الاستهزاء به / التجسس
function funcA() {}
exports.funcA = funcA;

function funcB() {
  exports.funcA(); // Now, this `exports.funcA` is added a spy or mocked. Keep the same reference to `funcA`
}
exports.funcB = funcB;

أو،

export let funcA = () => {};
export const funcB = () => {
  exports.funcA();
};

نتائج اختبار الوحدة:

 PASS  myModule.test.ts (9.225s)
  funcB
    ✓ should call funcA (3ms)

-------------|---------|----------|---------|---------|-------------------
File         | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
-------------|---------|----------|---------|---------|-------------------
All files    |     100 |      100 |     100 |     100 |                   
 myModule.ts |     100 |      100 |     100 |     100 |                   
-------------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        10.967s
  1. استخدم حزمة تجديد الأسلاك للسخرية من funcA
    ...

بالإضافة إلى ذلك ، تحتاج إلى إلقاء نظرة على هذه المستندات: https://nodejs.org/api/modules.html#modules_exports_shortcut ، لمعرفة ما يفعله require بالضبط

نجح الحل في هذا المنشور المكدس
https://stackoverflow.com/a/53402206/1217998

تقوم أولاً بتحويل جميع الوظائف التي تريد تحويلها إلى jest.fn

jest.mock('../../utils', () => {
  const actualUtils = jest.requireActual('../../utils');
  const originalImplementation = actualUtils.someFun;

  return {
    ...actualUtils,
    someFun: jest.fn()
      .mockImplementation(originalImplementation),
  };
});
const utils = require('../../utils');

ثم يمكنك استخدامه كالمعتاد إذا كنت تريد أو تسخر منه هكذا

jest.spyOn(utils, 'someFun').mockReturnValueOnce(true);

يمكنك استخدام هذا للحصول على التطبيق الأصلي

beforeEach(() => {
    jest.clearAllMocks();
  });

نجح الحل في هذا المنشور المكدس
https://stackoverflow.com/a/53402206/1217998

تقوم أولاً بتحويل جميع الوظائف التي تريد تحويلها إلى jest.fn

jest.mock('../../utils', () => {
  const actualUtils = jest.requireActual('../../utils');
  const originalImplementation = actualUtils.someFun;

  return {
    ...actualUtils,
    someFun: jest.fn()
      .mockImplementation(originalImplementation),
  };
});
const utils = require('../../utils');

ثم يمكنك استخدامه كالمعتاد إذا كنت تريد أو تسخر منه هكذا

jest.spyOn(utils, 'someFun').mockReturnValueOnce(true);

يمكنك استخدام هذا للحصول على التطبيق الأصلي

beforeEach(() => {
    jest.clearAllMocks();
  });

شكرا لك!

لدى Jest مثال بسيط جدًا وواضح في توثيقهم لكيفية السخرية من وحدة ما جزئيًا. يعمل هذا مع كل من عبارات ES import و Node request.
https://jestjs.io/docs/en/jest-object#jestrequireactualmodulename

لا يعمل عند استدعاء الوظيفة التي تم الاستهزاء بها من داخل الوحدة النمطية.

وجدت أيضًا أنه في بعض الأحيان قد يكون من المفيد محاكاة الوظيفة بالطريقة التي لا تغير بها الوظيفة الأصلية ولكن تستدعي الوظيفة ببعض المتغيرات المخصصة (الإضافية):

jest.mock('./someModule', () => {
  const moduleMock = require.requireActual('./someModule');
  return {
    ...moduleMock,
    // will mock this function 
    someFunction: (args) =>
      moduleMock.someFunction({
        ...args,
        customArgument,
      }),
  };
});

في حالتي ، كنت بحاجة إلى تمرير config إلى العمل والذي بدونه سيستخدم الإعداد الافتراضي.

هذه هي الطريقة الوحيدة التي وجدتها للقيام بذلك ، لذلك إذا توصلت إلى بعض الأفكار الأفضل أو الأسهل ، فسيسعدني سماعها :)

FWIW لقد جمعت طرقًا مختلفة مع أمثلة قابلة للتشغيل في https://github.com/magicmark/jest-how-do-i-mock-x/blob/master/src/function-in-same-module/README. م

هذا لا يجيب على سؤال / مشكلة OPs ، ولكنه حل يتضمن بعض إعادة البناء. لقد وجدت أن فصل وظائفي إلى ملفات مختلفة ، ثم الاستهزاء بهذه الواردات ، هو أسهل شيء أقوم به.

// package.json
...
"scripts": {
    "test": "jest",

...
"devDependencies": {
    "@babel/preset-env": "^7.11.5",
    "jest": "^24.9.0",
...

شبيبة
// babel.config.js

module.exports = {
الإعدادات المسبقة: [
[
"@ babel / preset-env" ،
{
الأهداف: {
العقدة: "تيار" ،
} ،
} ،
] ،
] ،
} ؛

```js
// module-utils.js

export const isBabaYaga = () => {
  return false
}

// module.js

import { isBabaYaga } from './module-utils'

export const isJohnWickBabaYaga = () => {
  return isBabaYaga()
}
// modules.test.js

import { isJohnWickBabaYaga } from './module';

jest.mock('./module-utils', () => ({
    isBabaYaga: jest.fn(() => true)
}))

test('John Wick is the Baba Yaga', () => {

    // when
    const isBabaYaga = isJohnWickBabaYaga()

    // then
    expect(isBabaYaga).toBeTruthy()
});
PASS  src/module.test.js
✓ John Wick is the Baba Yaga (4ms)

لقد واجهت هذه المشكلة مؤخرًا. لا يعمل أي من الحلول المقترحة بالنسبة لي لأنني غير قادر على تغيير الكود. لا يعمل برنامج babel-plugin-Rewire أيضًا بالنسبة لي. هل هناك أي حلول أخرى لاختبار أن وظيفة ما تم استدعاؤها بواسطة وظيفة أخرى داخل نفس الوحدة؟ يبدو هذا بصراحة أنه يجب أن يعمل أو أنه يجب أن يكون هناك مكون إضافي بابل يقوم بذلك. أي مساعدة سيكون محل تقدير كبير!

لقد واجهت هذه المشكلة مؤخرًا. لا يعمل أي من الحلول المقترحة بالنسبة لي لأنني غير قادر على تغيير الكود. لا يعمل برنامج babel-plugin-Rewire أيضًا بالنسبة لي. هل هناك أي حلول أخرى لاختبار أن وظيفة ما تم استدعاؤها بواسطة وظيفة أخرى داخل نفس الوحدة؟ يبدو هذا بصراحة أنه يجب أن يعمل أو أنه يجب أن يكون هناك مكون إضافي بابل يقوم بذلك. أي مساعدة سيكون محل تقدير كبير!

هل قمت بفحص https://github.com/facebook/jest/issues/936#issuecomment -659597840؟ هناك حد أدنى من التكرار هناك تستدعي وظائف mocks في نفس الملف.

هل كانت هذه الصفحة مفيدة؟
0 / 5 - 0 التقييمات