Jest: Как издеваться над конкретной функцией модуля?

Созданный на 25 апр. 2016  ·  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

Другие среды тестирования, такие как mock Python (https://docs.python.org/3/library/unittest.mock-examples.html), позволяют имитировать определенные функции. Это фундаментальная концепция тестирования.

Я настоятельно рекомендую имитировать часть модуля. Я думаю, что jest-mock следует изменить, чтобы условно игнорировать экспорт из издевательства и ссылаться на исходную реализацию.

Ты можешь сделать:

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 он ссылается, _ не является вашей перезаписанной версией_. Если вы мне не верите, можете проверить это.

Следовательно, описанный вами пример не подходит для моей проблемы. Вы знаете что-то, чего я не знаю?

@cpojer

Думаю, я хорошо это понимаю. Однако то, как babel компилирует модули, не упрощает понимание, и я понимаю ваше замешательство. Я не знаю точно, как это будет вести себя в реальной среде ES2015 с модулями, в основном потому, что такой среды сейчас не существует (кроме, возможно, последних версий Chrome Canary, которые я еще не пробовал). Чтобы объяснить, что происходит, мы должны взглянуть на скомпилированный вывод вашего кода babel. Это будет выглядеть примерно так:

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 могут иметь неизменяемые привязки к тому, что они экспортируют, базовый скомпилированный код, в который сейчас компилируется babel, не налагает никаких таких ограничений. В настоящее время я не вижу возможности поддерживать именно то, что вы просите, в строгой модульной среде ES2015 с модулями, поддерживаемыми изначально. Принцип работы jest-mock заключается в том, что он запускает код модуля изолированно, а затем извлекает метаданные модуля и создает фиктивные функции. Опять же, в этом случае у него не будет возможности изменить локальную привязку foo . Если у вас есть идеи о том, как эффективно реализовать это, пожалуйста, поделитесь здесь или отправьте запрос на перенос. Я хотел бы напомнить вам, что у нас есть кодекс поведения для этого проекта, с которым вы можете ознакомиться здесь: https://code.facebook.com/pages/876921332402685/open-source-code-of-conduct

Правильное решение в вашем примере - не издеваться над foo, а издеваться над высокоуровневым API, который вызывает foo (например, XMLHttpRequest или абстракцией, которую вы используете вместо этого).

@cpojer Спасибо за подробное объяснение. Мне очень жаль, если я обидел вас своим языком, я очень эффективно пишу инженерные тексты и хочу изложить свою точку зрения как можно скорее. Чтобы представить ситуацию в перспективе, я потратил 5 часов, пытаясь разобраться в этой проблеме, и написал 2 подробных комментария, а затем вы закрыли его коротким сообщением, в котором полностью упускается суть обоих моих утверждений. Вот почему в моем следующем сообщении говорилось, что у вас «фундаментальное недоразумение», потому что либо 1) вы не поняли, о чем я говорил, или 2) вы не поняли require() , что, к счастью, было вариантом 1.

Я подумаю о возможном решении моей проблемы, чтобы обойти это, пока я издевался над API нижнего уровня, но определенно должен быть способ имитировать функцию напрямую, так как это было бы весьма полезно.

Я согласен, что было бы полезно иметь возможность сделать это, но в 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, а издеваться над высокоуровневым API, который вызывает foo (например, XMLHttpRequest или абстракцией, которую вы используете вместо этого).

Я новичок в Jest, и я борюсь с аналогичной проблемой. Я использую аксиомы , которые под капотом используют XMLHttpRequest , и я не хочу издеваться над axios , а хочу высмеивать фактический XMLHttpRequest . Кажется , что я должен был бы реализовать свои методы по себе, что - то вроде этого . Это правильный подход?

Спасибо!

да, что-то вроде этого должно направить вас на верный путь! :) Однако используйте jest.fn как более приятный API: D

@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")

Для всех, кто сталкивается с этим в поисках решения, мне кажется, что следующее работает для меня при экспорте многих констант / функций в один файл и импорте их в файл, который я тестирую.

`` 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-react-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 и вашим методом тестирования, чтобы шутка проверила, был ли он вызван. Если вы можете предоставить фрагмент своего издевательского кода, я могу проверить, что не так.

@ainesophaur ... ммм. Я думал, что ваши коды выше предназначены для издевательства над методом test() ? эта часть: test: jest.fn(() => {console.log('I didnt call the original')}),

@ainesophaur Я

Я думаю, что это свойственно тому, как require работает, как указано выше ... Я бы хотел, чтобы для этого было решение.

@cpojer Есть что-нибудь новое относительно частично имитирующих модулей?

@rantonmattei & @huyph Мне нужно было бы увидеть отрывок из ваших фиктивных определений и теста, который вы выполняете. Вы должны определить свой макет до того, как потребуется / импортировать фактический файл реализации. Прошло некоторое время с тех пор, как я работал с JEST, но я помню, что в конечном итоге я получил его для имитации всего, что мне было нужно, будь то библиотека node_modules или файл в моем приложении. У меня немного не хватает времени на банкомат, но вот некоторые тесты из проекта, над которым я работал с помощью Jest.

Мокинг файла из зависимости

Фактическое определение функции в этом примере выполняется с помощью react-native .. Я издеваюсь над файлом «react-native / Libraries / Utilities / dismissKeyboard.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;

Я не могу найти тестовый файл, который я использовал для вышеупомянутого, но это было что-то вроде require the module, jest нашел бы его в __mocks__, а затем я мог бы сделать что-то вроде

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. Я все еще не могу заставить его работать с jest 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(...) {
....
}

Я не могу найти способ решить эту проблему. Могу я снова открыть это, пожалуйста? @cpojer

@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() в другую const. Спасибо

Я так понимаю, что это не определено в рамках saveSession. saveSession - это
вызов метода родственника в родительской области. Я сталкивался с этим много раз
и то, что я предложил, сработало

8 мая 2017 г. в 20:38 "Хай Фам" [email protected] написал:

Я должен был добавить, что restartCheckExpiryDateTimeout () - это экспортированный
функция. Не локально определенная функция в saveSession () ...

Я попробую то, что вы предлагаете выше. Спасибо

-
Вы получаете это, потому что вас упомянули.
Ответьте на это письмо напрямую, просмотрите его на GitHub
https://github.com/facebook/jest/issues/936#issuecomment-300029003 или отключить звук
нить
https://github.com/notifications/unsubscribe-auth/AEeBdsmpOOmzvcUHB3D_-Z7MChIzt10Pks5r37WYgaJpZM4IPGAH
.

Просто попробовал ... Я обнаружил, что:

Это НЕ работает: (т.е. исходный restartCheckExpiryDateTimeout () все еще вызывается)

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

Это работает: (т.е. вместо этого вызывается фиктивный restartCheckExpiryDateTimeout ()). Разница заключается в использовании function() вместо формы стрелки и использовании this. вместо session.

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

Это может быть проблема с пересылкой этих кодов в шутку ....

Попробуйте экспортировать их как объект класса вместо pojo. Я верю
Транспилер поднимает переменные по-другому. Мы перейдем к рабочему
тест, обещаю .. Прошло около полугода с тех пор, как я был на проекте
использовал шутку, но я хорошо помню эту проблему, и в конце концов я вспоминаю
найти решение.

9 мая 2017 года в 00:53 "Хай Фам" [email protected] написал:

Просто попробовал ... Я обнаружил, что:

Это НЕ работает: (т.е. исходный restartCheckExpiryDateTimeout ()
все еще звонят)

экспортная сессия = {
saveSession: () => {
session.restartCheckExpiryDateTimeout ();
},
restartCheckExpiryDateTimeout: () => {},
}

Это НЕ работает: (т.е. исходный restartCheckExpiryDateTimeout ()
все еще звонят)

экспортная сессия = {
saveSession: function () {
this.restartCheckExpiryDateTimeout ();
},
restartCheckExpiryDateTimeout: () => {},
}

Это может быть проблема с пересылкой этих кодов в шутку ....

-
Вы получаете это, потому что вас упомянули.
Ответьте на это письмо напрямую, просмотрите его на 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 часа ночи "novaline" [email protected] написала:

@ainesophaur https://github.com/ainesophaur не работает.

модуль:

экспорт const getMessage = (число: число): строка => {
return Her name is ${genName(num)} ;
};
функция экспорта genName (num: number): string {
вернуть novaline;
}

тестовое задание:

function mockFunctions () {
const original = require.requireActual ('../ moduleA');
возвращение {
... оригинал,
genName: jest.fn (() => 'Эмили')
}
} jest.mock ('../ moduleA', () => mockFunctions ()); const moduleA = require ('../ moduleA');
описать ('фиктивная функция', () => {

it ('t-0', () => {
ожидать (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
● имитация функции ›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

Что я делаю, так это создаю константную оболочку для функций, а затем экспортирую эту оболочку (например, export const fns). Затем внутри модуля используйте fns.functionName, а затем я могу jest.fn () функцию fns.functionName

Когда мы пишем фиктивную функцию пользовательского модуля, который написан на машинописном тексте, и когда мы вызываем фиктивную функцию, это исходная функция, описанная в отчете о покрытии, потому что мы вызываем фиктивную версию функции.

У меня есть 2 функции, которые изначально были импортированы в тесты как
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

@miluoshi @Rdlenke, если ваш код состоит из именованных экспортов, вы также можете import * as model а затем перезаписать model.generateImagePreview = jest.fn(() => Promise.resolve);

Как бы вы это проверили с помощью sinon? Как упоминалось ранее (см. Https://github.com/facebook/jest/issues/936#issuecomment-214939935), способ работы ESM делает невозможным издевательство над func2 пределах func1 , поэтому Я бы не назвал это базовым.

Может быть, можно написать мод babel, который читает в любых функциях "testImport"
и переписывает код для экспорта функций в модуле до
тестовый запуск?

В понедельник, 18 декабря 2017 г., в 17:00 Джим Муди [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 | Зарегистрированные офисы: ЛЮКС 1 СЕКУНДА
ЭТАЖ 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 мой герой!

Следуя ответу @dinvlad , я думаю, что добавление, показ на примере или ссылка на следующие макетные документы на странице объекта шутки на страницу макетных функций может быть улучшением шутливых документов по макету:

  • jest.isMockFunction (функция)
  • jest.genMockFromModule (имя модуля)
  • jest.mock (имя модуля, фабрика, параметры)
  • jest.unmock (имя модуля)
  • jest.doMock (имя модуля, фабрика, параметры)
  • jest.dontMock (имя_модуля)

Мой вариант использования заключается в том, что как новый пользователь jest я переношу код mocha + sinon.js на jest. У меня уже были шпионы и ожидания, поэтому я подумал, что это будет легко. Но после прочтения этой ветки и прочтения шутливой документации по фиктивным функциям у меня сложилось впечатление, что использование шутки таким образом может потребовать переписывания моих тестов или подробного понимания ESM или Babel ... или другой путаницы.

Спасибо за Jest - это упрощает написание / понимание моих тестов и ускорение их выполнения. :)

PR, разъясняющий документы, очень приветствуется! 🙂

Чтобы имитировать только определенные модули с синтаксисом модуля ES, вы можете использовать require.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()
      })
    })

errorHandler

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() ? Как человек из Jasmine, я обнаружил, что этот совет имеет решающее значение для достижения той же функциональности, что и шпионы Jasmine. Например

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

Если вы _не_ используете mockImplementation() , тогда jest.spyOn() создаст объект, который _не_ имитация (afaiu), и он фактически подчиняется нативной реализации. Если вам все же нужно сохранить нативную реализацию, возможно, стоит использовать

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

Не уверен, что это необходимо, но уверен, что это _должно_ работать.

Возвращаясь к исходному запросу ...

Не могли бы вы просто обернуть прокси-сервер вокруг импорта *? например

import * как тест из './myfile.js';

const handler = {
/ ** Перехваты: получение свойств * /
get (target, propKey, Receiver) {
console.log ( GET ${propKey} );
return 123;
},

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

const p = новый прокси (тест);

Вт, 30.01.2018, 16:24, Денис Логинов [email protected]
написал:

@WangHansen https://github.com/wanghansen не могли бы вы добавить
.mockImplementation () в jest.spyOn ()? Как кто-то из
Жасмин, я нашел этот совет очень важным для достижения той же функциональности, что и
Шпионы Жасмин. Например

const mockModuleFunction = шутка
.spyOn (модуль, 'функция')
.mockImplementation (() => 'привет'); ... ожидать (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 | Зарегистрированные офисы: ЛЮКС 1 СЕКУНДА
ЭТАЖ EVERDENE HOUSE, DEANSLEIGH ROAD, BOURNEMOUTH, ВЕЛИКОБРИТАНИЯ, BH7 7DU

@dinvlad @iampeterbanjo @SimenB Еще раз спасибо за вашу помощь, но, к сожалению, ни один из предложенных вами способов не сработал. Интересно, не потому ли, что функция вызывается в виде next(err) . Логика такова, что если запрос не удался, он будет передан в errorHandler путем вызова return next(err) . Наверняка функция вызывается, потому что, когда я добавил console.log , она печатает. Но тесты показывают, что это никогда не вызывается

@dinvlad Я попробовал ваш метод, он не сработал, но все равно спасибо за вашу помощь. Мне просто интересно, почему вам нужно вызывать mockImplementation , согласно официальному документу на jest.spyOn , вы вызываете его только тогда, когда хотите переопределить исходную функцию.

@WangHansen да, вы правы, что это нужно только тогда, когда кто-то хочет переопределить исходный метод. Я просто подбрасывал идею для таких ситуаций.

Одна из причин, по которой у вас это могло не получиться, - асинхронность. Если ваш метод использует обратные вызовы и / или обещания (или async / await), вам необходимо убедиться, что ваши ожидания действительно выполняются, прежде чем ваш тестовый метод завершится. Для подтверждения этого есть специальный метод expect.assertions(N) . Также убедитесь, что ваше ожидание выполняется только после вызова кода внутри callbacks / promises. Я уверен, что вы смотрели на это, но только для справки, https://facebook.github.io/jest/docs/en/asynchronous.html

К сожалению, имитировать внутренне используемые fns, как описывает

На мой взгляд, тесты не должны определять, как реализуется бизнес-логика 😕 (по крайней мере, не в этой степени)

Есть ли планы реализовать такое поведение в шутку?

Привет,
Довольно поздно для всей дискуссии, но, прочитав всю дискуссию, мне все еще не удалось этого сделать.
Многообещающее решение @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.
Мне потребовалось время, чтобы найти этот плагин, но это было решение, достаточно связное, чтобы не чувствовать себя хакерским.

В большинстве случаев, мы хотим , чтобы не издеваться один или несколько функций из модуля. Если вы используете систему насмешек global jest, вы можете добиться этого с помощью 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;

Это решение позволяет использовать макеты для других функций из модуля, используя исходную реализацию someFunction когда макетируется весь модуль, а также позволяет имитировать функцию someFunction с помощью mockImplementationOnce или mockImplementation API.

Я прочитал все разговоры выше, но мне не подходит ни одно решение.
Если вы все еще ищете решение для этого тестового примера, ответ - babel-plugin-rewire , этот плагин предназначен для разрешения того сценария, который мы обсуждали.
Пожалуйста, взгляните на эту библиотеку, вы поблагодарите меня позже.

Итак, чтобы резюмировать всю цепочку выше:

  1. Предположим, у вас есть модуль m с функциями f , g и h где g и h call f . Мы хотели бы издеваться над f чтобы g и h вызывали макет вместо настоящего f . К сожалению, это невозможно сделать напрямую, если f всегда не вызывается через exports как описано в
  2. Однако предположим, что мы перемещаем f в другой модуль m2 . Тогда m будет иметь выражение типа import {f} from 'm2' и когда g и h вызывают f , они фактически вызывают m2.f where m2 = require('./m2') (так будет выглядеть перевод Babel / TypeScript). Это позволяет надежно имитировать f как описано greypants . Другими словами, вы можете надежно имитировать вызовы, только если они пересекают границу модуля . Примечание. Решение greypants теперь выдает следующее сообщение об ошибке: «Фабрика модулей jest.mock() не может ссылаться на какие-либо переменные вне области видимости - Недопустимый доступ к переменной: __assign». Я подозреваю, что это ошибка Jest; в качестве обходного пути используйте Object.assign как показано ниже.
  3. Но если вместо имитации одной или двух функций вы хотите имитировать все, кроме одной или двух функций, используйте код вроде darkowic .

Пример (2):

~~~ js
// модуль m.js
импортировать {f} из './m2'
функция экспорта g () {return 'f return' + f (); };

// модуль m2.js
функция экспорта f () {возвращает "реальное f"; }

// test.js
import * как m из './m'

jest.mock ('./ m2', () => Object.assign (
require.requireActual ('./ m2'), {
f: jest.fn (). mockReturnValue ('МОК')
}));

test ('mocked', () => {
ожидать (мг ()). toEqual ('f вернул MOCK');
});
~~~

Во время тестирования я столкнулся с # 2649: вызов jest.mock внутри теста не имеет никакого эффекта, и если вы вызываете его в глобальной области видимости, вы не можете unmock перед другими тестами. Очень назойливый.

Спасибо!! @sarahdayan
Давно искал это

Если документов не хватает, PR всегда рады их уточнить 🙂

Всем привет!

Я немного поигрался и у меня возникла следующая идея решить эту проблему:

  • имитируйте модуль, но у него есть оригинальный модуль в цепочке прототипов.
  • предоставить метод для добавления свойств в макет модуля (который переопределит свойства из прототипа)
  • также предоставьте метод удаления свойств из имитируемого модуля (чтобы снова использовать свойства из прототипа).
// 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 цента:

Я протестировал множество решений (если не все), и единственное, что сработало для меня, это одно (шутка 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?

Это сработало для меня, основываясь на ответе @thomaskempel :

В моем случае я хотел имитировать зависимость в node_modules, позвольте называть это «shared-components». Он экспортирует ряд названных компонентов. Я хотел издеваться только над парочкой из этих названных экспортов, а остальные оставить как настоящие.

Итак, в __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-functions-within-a-single- модуль-в-jest-cdf2b61af642

На основе решения

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

@MajorBreakfast , который работает с 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}

Теперь вы можете импортировать все функции из файла в свой тест-шутку по умолчанию;

dataDao.spec.js

import * as dataDao из '../dataDao';
// Шпионить за модулями, ссылающимися на назначенное имя по умолчанию при импорте
jest.spyOn (dataDao, 'getData')
jest.spyOn (dataDao, 'setData')
jest.spyOn (dataDao, 'deleteData')

@vchinthakunta , это может работать, но похоже на нарушение основной цели синтаксиса экспорта / импорта: другие модули больше не смогут импортировать определенные методы или поля данных через

import { justThisThing } from 'someModule';

Я что-то там упускаю?

@MajorBreakfast , который работает с 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')
  })
})

Спасибо!

Я хотел издеваться только над одним методом lodash, например lodash.random, и смог легко сделать это с помощью:

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 Вы когда-нибудь находили решение своего примера? Насколько я могу судить, то, что вы пытаетесь сделать, не поддерживается издевательством через Jest. В частности, я думаю, что насмешка - это, по сути, изменение импорта, чтобы внешние представления модуля видели фиктивные методы. Однако в вашем примере foo и bar находятся в одном модуле, поэтому представление foo для bar невозможно высмеять.

Я считаю, что у вас есть следующие варианты:
1) Реорганизуйте свой код так, чтобы foo импортировал модуль, включающий bar
2) Используйте babel-plugin-rewire

Пожалуйста, поправьте меня, если я неправильно понял!

У меня было несколько иное требование: я хотел имитировать весь модуль _except_ для одной функции. Используя решение @MajorBreakfast в качестве отправной точки, я пришел к следующему:

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

@dgrcode Вы когда-нибудь находили решение своего примера? Насколько я могу судить, то, что вы пытаетесь сделать, не поддерживается издевательством через Jest. В частности, я _ думаю_, что насмешка - это просто перемонтаж импорта, чтобы внешние представления модуля видели макетированные методы. Однако в вашем примере 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

Это требует, чтобы вы поручили использование выражений функций вместо объявлений . К счастью, правило func-style eslint вас поддержало.

Использование 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 babel

Если вы не хотите предписывать использование функциональных выражений (например, используя const ), это может быть хорошим подходом.

Это позволяет вам использовать функции _rewire_ (также известные как mock) из одного модуля. Я мог представить, что код будет выглядеть примерно так, как показано ниже, но не тестировал его. Также из их документации похоже, что вы можете перепрограммировать функции в том же модуле, которые даже не экспортируются из модуля 👍, представьте себе пример ниже без экспорта функции 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');
  });
});

Примеры см. В документации .

Примечание: требуется транспиляция Babel

Вариант 3 - разделить все функции на отдельные модули / файлы

Этот вариант наименее благоприятен, но он явно отлично работает с типичной функциональностью mock .


PS: Хотя этот шаг был очень поучительным и часто развлекательным, я надеюсь, что этот синопсис смягчит необходимость для других прочитать всю ветку. ✌️

Спасибо, @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 . Я не уверен, как настроена шутка в react-scripts но я думаю, что есть способ как-то обновить конфигурацию.

Это исправление удаляет преобразования для файлов *.css и *.svg , поэтому ошибки App.tsx игнорируются.

Есть ли что-то особенное, что нужно сделать, чтобы заставить его работать?
Я бы сказал, что у меня довольно стандартная установка (без ts), и она не работает из коробки.

Я посмотрю на это еще немного сегодня вечером и посмотрю, возможно ли это.

@danielhusar Я заглянул вчера вечером и не смог получить готовое решение. Ключ - это шутливая конфигурация transformer которую CRA позволяет вам переопределить в package.json#jest . Файлы js и ts передаются с использованием babel-jest но react-scripts блокирует вас от использования файла конфигурации .babelrc и установки test env, который они установили в 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) function.

У меня есть вариант 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, так и с операторами Node require.
https://jestjs.io/docs/en/jest-object#jestrequireactualmodulename

@johncmunson Это хороший jest.mock _once_, и ни один из имитируемых методов не использует другой экспорт из этого модуля.

Возьмем пример сверху ... Я добавил 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 и все равно получил тот же результат.


Нажмите, чтобы увидеть код

`` тс
импортировать * как testModule из './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 а не макет.

Я бы хотел, чтобы это было так просто, но из того, что я вижу, это не помогает для примеров из этой ветки. Если мне что-то здесь не хватает, дайте мне знать. Я с радостью признаю свою вину.

Для всех, кто сталкивается с этим в поисках решения, мне кажется, что следующее работает для меня при экспорте многих констант / функций в один файл и импорте их в файл, который я тестирую.

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';

Это позволяет легко восстановить исходные функции и очистить макеты.

Для всех, кто сталкивается с этим в поисках решения, мне кажется, что следующее работает для меня при экспорте многих констант / функций в один файл и импорте их в файл, который я тестирую.

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. используйте пакет rewire для имитации funcA
    ...

Кроме того, вам нужно взглянуть на эти документы: https://nodejs.org/api/modules.html#modules_exports_shortcut , чтобы узнать, что именно делает require

Решение в этом сообщении stackoverflow сработало для меня
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();
  });

Решение в этом сообщении stackoverflow сработало для меня
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, так и с операторами Node require.
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,
      }),
  };
});

В моем случае мне нужно было передать конфигурацию для работы, без которой она будет использовать конфигурацию по умолчанию.

Я нашел единственный способ сделать это, поэтому, если у вас возникнут какие-то лучшие или более простые идеи, буду рад услышать :)

FWIW Я объединил различные подходы с запускаемыми примерами в https://github.com/magicmark/jest-how-do-i-mock-x/blob/master/src/function-in-same-module/README. мкр

Это не отвечает на вопрос / проблему OP, но является решением с некоторым рефакторингом. Я обнаружил, что разделение моих функций на разные файлы, а затем имитация импорта - это самый простой способ.

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

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

`` js
// babel.config.js

module.exports = {
предустановки: [
[
'@ babel / preset-env',
{
target: {
узел: 'текущий',
},
},
],
],
};

```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, который делает это. Любая помощь приветствуется!

Я недавно столкнулся с этой проблемой. Ни одно из предложенных решений у меня не работает, потому что я не могу изменить код. Babel-plugin-rewire у меня тоже не работает. Есть ли какие-либо другие решения для проверки того, что функция была вызвана другой функцией в том же модуле? Честно говоря, кажется, что он должен работать или что должен быть плагин babel, который делает это. Любая помощь приветствуется!

Вы проверяли https://github.com/facebook/jest/issues/936#issuecomment -659597840? Там есть минимальное воспроизведение, которое имитирует вызовы функций в том же файле.

Была ли эта страница полезной?
0 / 5 - 0 рейтинги