Jest: How to mock specific module function?

Created on 25 Apr 2016  ·  116Comments  ·  Source: facebook/jest

I'm struggling with something that I think should be both easy and obvious, but for whatever reason I can't figure it out.

I have a module. It exports multiple functions. Here is myModule.js:

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

I unmock the module for testing.

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

However, I need to mock foo, because it makes ajax calls to my backend. I want every function in this file to remain unmocked, expect for foo, which I want to be mocked. And the functions bar and baz make calls internally to foo, so when my test calls bar(), the unmocked bar will call the mocked foo.

It appears in the jest documentation that calls to unmock and mock operate on the entire module. How can I mock a specific function? Arbitrarily breaking up my code into separate modules so they can be tested properly is ridiculous.

Most helpful comment

You can do:

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

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

See http://facebook.github.io/jest/docs/api.html#mock-functions

All 116 comments

Upon deeper analysis, it appears that jest-mock generates an AST for the whole module, then uses that AST to create a mocked module that conforms to original's exports: https://github.com/facebook/jest/tree/master/packages/jest-mock

Other testing frameworks, such as Python's mock (https://docs.python.org/3/library/unittest.mock-examples.html), let you mock specific functions. This is a fundamental testing concept.

I highly recommend the ability to mock a portion of a module. I think that jest-mock should be changed to conditionally ignore exports from mocking, and reference the original implementation.

You can do:

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

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

See http://facebook.github.io/jest/docs/api.html#mock-functions

I think you have a fundamental misunderstanding of how require works. When you call require(), you don't get an instance of the module. You get an object with references to the module's functions. If you overwrite a value in the required module, your own reference is overwritten, _but the implementation keeps the original references_.

In your example, if you call myModule.foo(), yes, you will call the mocked version. But if you call myModule.bar(), which internally calls foo(), the foo it references _is not your overwritten version_. If you don't believe me, you can test it out.

Therefore, the example you described is inadequate for the problem I have. Do you know something I don't?

@cpojer

I do believe I understand this quite well. The way babel compiles modules however doesn't make this easier to understand and I understand your confusion. I don't know exactly how this would behave in a real ES2015 environment with modules, mainly because no such environment exists right now (except maybe latest versions of Chrome Canary, which I haven't tried yet). In order to explain what happens, we have to look at the compiled output of your babel code. It will look something like this:

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

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

In this case, it is indeed correct that you cannot mock foo and I apologize for not reading your initial issue correctly, however it did not make any assumption on how foo was called, so I assumed it was exports.foo(). Supporting the above by mocking a function after requiring a module is impossible in JavaScript – there is (almost) no way to retrieve the binding that foo refers to and modify it.

However, if you change your code to this:

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

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

and then in your test file you do:

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

it will work just as expected. This is what we do at Facebook where we don't use ES2015.

While ES2015 modules may have immutable bindings for what they export, the underlying compiled code that babel compiles to right now doesn't enforce any such constraints. I see no way currently to support exactly what you are asking in a strict ES2015 module environment with natively supported modules. The way that jest-mock works is that it runs the module code in isolation and then retrieves the metadata of a module and creates mock functions. Again, in this case it won't have any way to modify the local binding of foo. If you do have ideas on how to effectively implement this, please contribute here or with a pull request. I'd like to remind you that we have a code of conduct for this project that you can read up on here: https://code.facebook.com/pages/876921332402685/open-source-code-of-conduct

The right solution in your example is not to mock foo but to mock the higher-level API that foo is calling (such as XMLHttpRequest or the abstraction that you use instead).

@cpojer Thank you for your detailed explanation. I'm sorry if I offended you with my language, I am very efficient with my engineering writing and I want to get my point across ASAP. To put things into perspective, I spent 5 hours of time trying to understand this issue and wrote 2 detailed comments, then you closed it with a brief message that completely missed the point of both of my statements. That is why my next message said you had a "fundamental misunderstanding", because either 1) you did not understand the point I was making, or 2) you didn't understand require(), which thankfully was option 1.

I will ponder a possible solution to my problem, to get around it for now I mocked a lower level API, but there should definitely be a way to mock the function directly, as that would be quite useful.

I agree it would be useful to be able to do this, but there isn't a good way in JS to do it without (probably slow) static analysis upfront :(

@cpojer: I'm unsure if jumping in here 5 months later is the way to go but I couldn't find any other conversations about this.

Jumping off from your suggestion above, I've done this to mock out one function from another in the same module:

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

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

    someModule.function1(...);

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

This works for the one test, but I haven't found a way to accomplish this in a way that's isolated to just the one it(...); block. As written above, it affects every test which makes it hard to test the real function2 in another test. Any tips?

You can call .mockClear on the function in beforeEach or call jest.clearAllMocks() if you are using Jest 16.

Hey @cpojer! I am using Jest 16. Neither jest.clearAllMocks() or someModule.function2.mockClear() work for me. They only work when the mock is an entire module, not a function of an imported module. In my project, the function remains mocked in subsequent tests. If this isn't expected I'll see if I can replicate in a small sample project and create a new issue. Good idea?

@cpojer -

The right solution in your example is not to mock foo but to mock the higher-level API that foo is calling (such as XMLHttpRequest or the abstraction that you use instead).

I'm new to Jest and I'm struggling with a similar problem. I'm using axios, which under the hood uses XMLHttpRequest, and I do not want to mock axios, but to mock the actual XMLHttpRequest. It seems that I would have to implement its methods by myself, something like this. Is this the right approach?

Thanks!

yeah, something like this should get you on the right path! :) Use jest.fn as a nicer API, though :D

@cpojer regarding your comment here: https://github.com/facebook/jest/issues/936#issuecomment-214939935

How would you do that with 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")

For anyone who comes across this looking for a solution, the following seems to be working for me when exporting many const/functions in one file, and importing them in a file which I'm testing

``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, not sure what I am doing wrong here. But it seems it doesn't work
I am currently on jest 18.1 (and 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();

Test will then fail with:

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

@huyph you'd have to mock your doSmth method and your test method in order for jest to test whether it was called. If you can provide the snippet of your mocking code I can check what's wrong

@ainesophaur ...uhm. I thought your codes above are for mocking the test() method? this part: test: jest.fn(() => {console.log('I didnt call the original')}),

@ainesophaur I tried your code as well. But it did not work for me. It never executes the mock function. So, the expectation is never met.

I think this is inherent to the way require works like stated above... I wish there was a solution for this.

@cpojer Is there anything new regarding partially mocking modules?

@rantonmattei & @huyph I'd have to see a snippet of your mock definitions and the test you're running. You have to define your mock before the actual implementation file is required/imported. Its been a while since I've worked with JEST, but I do remember I eventually got it to mock everything I needed to, whether it was a node_modules library or a file in my app. I'm a bit short on time ATM, but here is some of the tests from a project I worked on using Jest.

Mocking a file from a dependency

Actual function definition in this example is done by react-native.. I'm mocking the file "react-native/Libraries/Utilities/dismissKeyboard.js"

This is a mock file under __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;

I can't find the test file I used for the above, but it was something like require the module, jest would find it in __mocks__ and then I could do something like

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

Mocking a file you control
Actual function definition

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

Test file with mock

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});
});

thanks for sharing @ainesophaur. I still can't get it to work with jest 18.1. Here are my codes:

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();
});

In session.js

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

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

I can't find a way to resolve this issue. Can I reopen this please? @cpojer

@huyph the way you're doing the export saveSession is going to call the locally defined restartCheckExpiryDateTimeout instead of going through the module and calling module.restartCheckExpiryDateTimeout -- thus your mocked module.restartCheckExpiryDateTimeout wont be detected by saveSession as saveSession is calling the actual defined restartCheckExpiryDateTimeout function.

I'd assign saveSession to a const and then do saveSession.restartCheckExpiryDateTimeout = () => {...logic}. .then from within saveSession.saveSession, call saveSession.restartCheckExpiryDateTimeout instead of just restartCheckExpiryDateTimeout. Export your new const instead of the actual function saveSession which then defines your methods. Then when you call your someVar.saveSession() it'll then internally call saveSession.restartCheckExpiryDateTimeout() which is now mocked.

I should have added that restartCheckExpiryDateTimeout() is an exported function. Not a locally defined function within saveSession()... (Updated my comment above). In that case, I think module.saveSession() should call the right module.restartCheckExpiryDateTimeout()which is mocked.

But I will give what you suggest above a go though. Moving both saveSession() and restartCheckExpiryDateTimeout() to another const. Thanks

I understand it's not defined in the scope of saveSession. saveSession is
calling the sibling method in the parent scope. I ran into this many times
and what I suggested worked for it

On May 8, 2017 8:38 PM, "Huy Pham" notifications@github.com wrote:

I should have added that restartCheckExpiryDateTimeout() is an exported
function. Not a locally defined function within saveSession()...

I will give what you suggest above a go though. Thanks


You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/facebook/jest/issues/936#issuecomment-300029003, or mute
the thread
https://github.com/notifications/unsubscribe-auth/AEeBdsmpOOmzvcUHB3D_-Z7MChIzt10Pks5r37WYgaJpZM4IPGAH
.

Just tried that..I found that:

This does NOT work: (i.e the original restartCheckExpiryDateTimeout() is still called)

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

This works: (i.e the mock restartCheckExpiryDateTimeout() is called instead). The difference is the use of function() instead of arrow form, and the use of this. instead of session.

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

It could be a problem with jest transpiling these codes ....

Try to export them as a class object instead of pojo. I believe the
transpiler does hoist the variables differently. We will get to a working
test, I promise.. Its been about half a year since I was on the project
that used jest but I remember this problem well and I remember eventually
finding a solution.

On May 9, 2017 12:53 AM, "Huy Pham" notifications@github.com wrote:

Just tried that..I found that:

This does NOT work: (i.e the original restartCheckExpiryDateTimeout() is
still get called)

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

This does NOT work: (i.e the original restartCheckExpiryDateTimeout() is
still get called)

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

It could be a problem with jest transpiling these codes ....


You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/facebook/jest/issues/936#issuecomment-300060975, or mute
the thread
https://github.com/notifications/unsubscribe-auth/AEeBdrRQExycPYiGtvm7qYi5G87w6b6Oks5r3_FlgaJpZM4IPGAH
.

@sorahn same issue. es6 + babel , How to mock?
@cpojer Is that means es6 + babel , export const function xx() {} , export many function , Jest has no way to mock a function in a module(file) called by other function in the same module(file)? I test it, it seems I am correct. Just only for commonjs pattern, Jest can mock the function successfully, Like your example.

@ainesophaur not working.

module:

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

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

test:

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

  });

});

test result:

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

Look at my last few comments above. Specifically the last one. Your
exported methods are calling the locally scoped sibling method vs the
actual exported method (which is where your mock is)

On May 31, 2017 2:00 AM, "novaline" notifications@github.com wrote:

@ainesophaur https://github.com/ainesophaur not working.

module:

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

test:

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

});

});

test result:

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


You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/facebook/jest/issues/936#issuecomment-305091749, or mute
the thread
https://github.com/notifications/unsubscribe-auth/AEeBdv6SafXlTtKo3DNeFWhbL6gV9l0Gks5r_QHjgaJpZM4IPGAH
.

@ainesophaur : I attempted export class Session { }. And it doesn't work for me.

The only approach that works for me is in my comment above: where function syntax is used instead of arrow () =>. Here:

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

This is on Jest 20.0.3

What I do is create a const wrapper for the functions, and then export that wrapper (such as export const fns). Then inside the module use fns.functionName, and then I can jest.fn() the fns.functionName function

When we write mock function of a user-defined module which is written in typescript and when we call the mock function is the original function covered in coverage report because we are calling the mocked version of the function.

I have 2 functions that were originally imported in the tests as
import { getCurrentDate, getStartEndDatesForTimeFrame } from ./../_helpers/date';
As you see getStartEndDatesForTimeFrame is dependent on getCurrentDate. With the following setup the getCurrentDate test works well and uses the mocked version. On the other hand for some reason the getStartEndDatesForTimeFrame test doesn't use the mocked getCurrentDate but the original implementation so my test fails. I have tried many different setups (like Date.now = jest.fn(() => "2017-11-16T20:33:09.071Z"); but couldn't make it work. Any ideas?

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 :(
  });
});

So the getStartEndDatesForTimeFrame fails as it uses the current time not the mocked one.

I have managed to make it work by following a suggestion by @ainesophaur - by exporting all functions within an object and calling these exported object's methods instead of local scoped sibling methods:

// 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 That's the only way I was able to do it too. Is there any loss of performance or something like that when we use this method? It seems "wrong "to change the code so you can test it.

I would really like a way to just write:
jest.mock('src/folder/file.func, () => {return 'whatever i want'})

key piece here being the .func

@miluoshi @Rdlenke if your code consists of named exports you can also import * as model and then overwrite model.generateImagePreview = jest.fn(() => Promise.resolve);

How would you test that with sinon? Like mentioned before (see https://github.com/facebook/jest/issues/936#issuecomment-214939935), the way ESM works make it impossible to mock func2 within func1, so I wouldn't necessarily call it basic.

Maybe a babel mod could be written that reads in any "testImport" functions
and rewrites the code to export the functions in the module prior to the
test running?

On Mon, Dec 18, 2017 at 5:00 PM, Jim Moody notifications@github.com wrote:

You're right @SimenB https://github.com/simenb, I had changed something
in my test in between switching to Sinon which mae it look like it passed.
When I reverted that, it is still not working. I guess it's not a problem
that has been solved.


You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/facebook/jest/issues/936#issuecomment-352488400, or mute
the thread
https://github.com/notifications/unsubscribe-auth/AQRY9a5-s2_bjCWKNw5WiAJW-JeBf8W3ks5tBpoygaJpZM4IPGAH
.

--

Darren Cresswell
Contract Developer | Develer Limited
E-mail: [email protected]
Phone:
Website: http://www.develer.co.uk

Please consider the environment before printing this email
WARNING: Computer viruses can be transmitted via email. The recipient
should check this email and any attachments for the presence of viruses.
Develer Limited accepts no liability for any damage caused by any virus
transmitted by this email. E-mail transmission cannot be guaranteed to be
secure or error-free as information could be intercepted, corrupted, lost,
destroyed, arrive late or incomplete, or contain viruses. The sender
therefore does not accept liability for any errors or omissions in the
contents of this message, which arise as a result of e-mail transmission.

WARNING: Although Develer Limited has taken reasonable precautions to
ensure no viruses are present in this email, the company cannot accept
responsibility for any loss or damage arising from the use of this email or
attachments.

Develer Limited is a limited company registered in England and Wales. |
Company Registration No. 09817616 | Registered Offices: SUITE 1 SECOND
FLOOR EVERDENE HOUSE, DEANSLEIGH ROAD, BOURNEMOUTH, UNITED KINGDOM, BH7 7DU

thanks @ainesophaur for the workaround.

In case anyone finds a non async working example useful, here's mine:

//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 If I understood the issue correctly, this should work for your example:

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

(this _only_ works if the functions are exported as const's, like in your example)

@dinvlad my hero!

Following from @dinvlad 's answer, I think that adding, showing by example or linking the following mock related docs on the jest object page to the Mock functions page might be an improvement to the jest docs on mocking:

  • jest.isMockFunction(fn)
  • jest.genMockFromModule(moduleName)
  • jest.mock(moduleName, factory, options)
  • jest.unmock(moduleName)
  • jest.doMock(moduleName, factory, options)
  • jest.dontMock(moduleName)

My use case is that as a new user of jest I'm migrating some mocha + sinon.js code to jest. I already had spies and expectations so I thought it would be easy. But after reading this thread and reading the jest docs on Mock functions I was getting the impression that using jest this way might need a rewrite of my tests or detailed understanding of ESM or Babel ... or other confusion.

Thanks for Jest - it's making my tests easier to write/understand and faster to execute. :)

PR clarifying the docs is very welcome! 🙂

To mock only specific modules with ES module syntax, you can use require.requireActual to restore the original modules, then overwrite the one you want to mock:

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!');
});

Feels inverted, but it's the simplest way I've come across. Hat tip to @joshjg.

I am lost somewhere in the long discussion, I just had a question, is there anyway to testing if the actual implementation of the function is called?

From what I understand, if I need to use jest.fn() it will override the original function, but if I don't use it, the console would give me error saying it must be a jest.fn() function or a spy

I am trying to test a middleware where the request will be passed along, so if I mock it, all the logic will be lost and the data won't be passed to next middleware. If I don't mock it, by importing it, is there anyway I can test this function has been called?

You can use jest.spyOn, maybe? By default it calls the underlying function

Thanks for the help, I tried, but the test suggests that it was never called even though it was called because I put the console.log and it did print

test file

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

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 |     })

Is the function called handleClientError or logError?

@WangHansen From your example your code should be expect(errorHandler.handleClientError).toBeCalled() // > true

@WangHansen could you add .mockImplementation() to your jest.spyOn()? As someone coming from Jasmine, I found this tip crucial to achieve the same functionality as Jasmine's spies. E.g.

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

If you _don't_ use mockImplementation(), then jest.spyOn() produces an object that is _not_ a mock (afaiu) and it actually defers to the native implementation. If you do have to keep native implementation, maybe it's worth using

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

Not sure this is necessary, but fairly sure it _should_ work.

Going back to the original request...

Couldn't you just wrap a proxy around an import *? eg

import * as test from './myfile.js';

const handler = {
/** Intercepts: getting properties */
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 = new Proxy(test);

On Tue, Jan 30, 2018 at 4:24 PM, Denis Loginov notifications@github.com
wrote:

@WangHansen https://github.com/wanghansen could you add
.mockImplementation() to your jest.spyOn()? As someone coming from
Jasmine, I found this tip crucial to achieve the same functionality as
Jasmine's spies. E.g.

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

If you don't use mockImplementation(), then jest.spyOn() produces an
object that is not a mock (afaiu) and it actually defers to the native
implementation. If you do have to keep native implementation, maybe it's
worth using

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

Not sure this is necessary, but fairly sure it should work.


You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/facebook/jest/issues/936#issuecomment-361648414, or mute
the thread
https://github.com/notifications/unsubscribe-auth/AQRY9VXyHNYatwOOY6EV637WGQH9k5Plks5tP0I9gaJpZM4IPGAH
.

--

Darren Cresswell
Contract Developer | Develer Limited
E-mail: [email protected]
Phone:
Website: http://www.develer.co.uk

Please consider the environment before printing this email
WARNING: Computer viruses can be transmitted via email. The recipient
should check this email and any attachments for the presence of viruses.
Develer Limited accepts no liability for any damage caused by any virus
transmitted by this email. E-mail transmission cannot be guaranteed to be
secure or error-free as information could be intercepted, corrupted, lost,
destroyed, arrive late or incomplete, or contain viruses. The sender
therefore does not accept liability for any errors or omissions in the
contents of this message, which arise as a result of e-mail transmission.

WARNING: Although Develer Limited has taken reasonable precautions to
ensure no viruses are present in this email, the company cannot accept
responsibility for any loss or damage arising from the use of this email or
attachments.

Develer Limited is a limited company registered in England and Wales. |
Company Registration No. 09817616 | Registered Offices: SUITE 1 SECOND
FLOOR EVERDENE HOUSE, DEANSLEIGH ROAD, BOURNEMOUTH, UNITED KINGDOM, BH7 7DU

@dinvlad @iampeterbanjo @SimenB Thanks again for all your help, but unfortunnately, none of the ways you have suggested worked. I wonder if it is because that the function is called in the form of next(err). The logic is, when a request failed, it will be passed into the errorHandler by calling return next(err). For sure the function is being called because when I added console.log, it is printing. But the tests suggest it is never called

@dinvlad I tried your method, it didn't work, but thanks for your help anyway. I was just wondering why do you need to call mockImplementation, according to the official doc on jest.spyOn, you only call it when you want to override the original function.

@WangHansen yes you're right that it's only needed when one wants to override the original method. I was just throwing an idea for those situations.

One reason why it might have failed for you is asynchronicity. If your method is using callbacks and/or Promises (or async/await), then you need to make sure your expectations are actually executed before your test method terminates. There's a special method expect.assertions(N) to assert that. Also make sure that your expectation is executed only after the code inside callbacks/promises has been called. I'm sure you've looked at that but just for the reference, https://facebook.github.io/jest/docs/en/asynchronous.html

It's unfortunate that mocking internally used fns as @seibelj describes is not possible without changing the module impl.

In my opinion, tests should not drive how business logic is implemented 😕 (at least not to this degree)

Are there any plans to implement such behaviour in jest?

Hi,
Kinda late to the whole discussion, but reading through the whole discussion - I still haven't managed to do that.
@greypants 's promising solution didn't really work for me, as it still calls the original function.
Have anything changed since the beginning of this discussion? Am I missing something?

I adapted @greypants's solution a little bit and it worked for me. Here's my code if it can help someone:

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)
})

This still feels kinda hacky and the docs weren't that helpful. I'm grateful for Jest and the team behind it but it seems like a common use case that should at least have an "official" solution.

@sarahdayan, and whomever it may be of interest -
I've ended up using babel-plugin-rewire.
It took me a while to find that plugin, but 'twas a solution coherent enough to not feel hacky.

In most cases, we want to not mock one or more functions from a module. If you use global jest's mocking system you can achieve it using genMockFromModule and requireActual. Here is the example:

//  __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;

This solution allows using mocks for other functions from the module, using someFunction original implementation when the whole module is mocked and also allows to mock the someFunction function with mockImplementationOnce or mockImplementation API.

I have read all conversations above, but no one solution works for me.
If you are still looking for solution for this test case, answer is babel-plugin-rewire, this plugin is to resolve that scenario case we discussed.
Please take a look on that lib, you will thanks me back later.

So to summarize the whole thread above:

  1. Suppose you have a module m with functions f, g and h where g and h call f. We would like to mock f so that g and h call the mock instead of the real f. Unfortunately this is not possible to do directly unless f is always called through exports as cpojer described. This is impossible if your module uses ES6 import/export syntax in TypeScript (and I'm guessing the same is true in Babel).
  2. However, suppose we move f to another module m2. Then m will have a statement like import {f} from 'm2' and when g and h call f, they actually are calling m2.f where m2 = require('./m2') (that's how the Babel/TypeScript translation will look). This makes it possible to mock f reliably as described by greypants. In other words, you can mock calls reliably only if they cross a module boundary. Note: greypants' solution now produces this error message: "The module factory of jest.mock() is not allowed to reference any out-of-scope variables - Invalid variable access: __assign". I suspect this is a bug in Jest; as a workaround, use Object.assign as shown below.
  3. But if, instead of mocking one or two functions, you want to mock everything except one or two functions, use code like darkowic's.

Example of (2):

~~~js
// module m.js
import {f} from './m2'
export function g() { return 'f returned ' + f(); };

// module m2.js
export function f() { return 'the real f'; }

// test.js
import * as m from './m'

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

test('mocked', () => {
expect(m.g()).toEqual('f returned MOCK');
});
~~~

While testing this I ran into #2649: calling jest.mock inside a test has no effect, and if you call it at global scope then you cannot unmock before other tests. Very annoying.

Thanks!! @sarahdayan
Been looking for this for a while

If the docs are lacking, PRs are always welcome to clarify them 🙂

Hi everyone!

I played a little bit around and had the following idea to solve that problem:

  • mock a module, but the mocked module has the original module in the prototype chain.
  • provide a method to add properties to the mocked module (which will override the properties from the prototype)
  • also provide a method to remove the properties from the mocked module (to use the one from the prototype again).
// 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");
});

My 2 cents :

I tested a lot of solutions (if not all of them) and the only one that worked for me is this one (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");
});

I came to the conclusion that it's not a good idea to try to do this because :

  • test.js knows too much about implementation details of importedModule.js
  • the solution is to fragile and nobody will understand the purpose of the mockProxy by looking at importedModule.js

Has anyone found a solution for this that works?

I'm using:

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

@jamesone have you read this https://github.com/facebook/jest/issues/936#issuecomment-410080252 ?

This worked for me, based on the answer by @thomaskempel:

In my case, I wanted to mock a dependency in node_modules, lets call it 'shared-components'. It exports a number of named components. I wanted to mock only a couple of these named exports, and leave the rest as the real thing.

So in __mocks__/shared-components.js I have:

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

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

In my case I was stubbing out the implementations. Hopefully this helps someone in the future.

I've faced the same issue recently, the conversation in this thread helped me to understand better and I summarized my findings here https://medium.com/@DavideRama/mock-spy-exported-functions-within-a-single-module-in-jest-cdf2b61af642

Inspired by @qwertie's solution

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

@MajorBreakfast that does work with React.lazy?

const mockLazy = jest.fn();

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

I still get ReferenceError: React is not defined.

To mock an independent export function module:
Export all of the individual functions part of the default export from the file.

Example:
dataDao.js

function getData()
function setData()
function deleteData()
export {getData, setData, deleteData}

Now you can import all of the functions from the file into your jest test by default naming;

dataDao.spec.js

import * as dataDao from '../dataDao';
// Spy on the modules referencing the assigned default name at the import
jest.spyOn(dataDao, 'getData')
jest.spyOn(dataDao, 'setData')
jest.spyOn(dataDao, 'deleteData')

@vchinthakunta, that may work, but it looks like a violation of a major purpose of the export/import syntax: other modules will no longer be able to to import specific methods or data fields via

import { justThisThing } from 'someModule';

Am I missing something there?

@MajorBreakfast that does work with React.lazy?

const mockLazy = jest.fn();

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

I still get ReferenceError: React is not defined.

I think 'React' should be lowercase here as it's referencing the import?

jest.mock('react'...)

I got my code working using the following which is simpler than other solutions i've seen here. It doesn't require you to use require or set up default exports.

helpers/navigation.js

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

...

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

component that uses 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 is hoisted, use doMock or

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

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

This seems like a common enough problem. Is there something about this in the jest docs?

So, is there a solution for mocking a function within same module?

The following method worked for me, the trick is to reset the mocked function back at the end of the test.
This example mocks verify function from jsonwebtoken module.

  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
  })

The following method worked for me, the trick is to reset the mocked function back at the end of the test.
This example mocks verify function from jsonwebtoken module.

  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
  })

where are you using the const verify? anyways, this will only work if your mocked function isn't an exported const function

So, is there a solution for mocking a function _within same module_?

After a lot of searching, the solution for this issue is to store your exports in a single object that your function and mocks can reference to. The below articles all came to the same consensus.

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

It's surprising to me that no one has mentioned a different solution that, if it is workable for your code, completely eliminates all these problems and makes testing the desired code very, very simple:

_Move the single function you want to mock into its own module._

Seriously. If your module is written such that you need to test inner parts of it separately from other inner parts, then almost certainly your class is violating the Single Responsibility Principle (yes, it's not a real class, but the module is functioning like a class, modules being a unit container of code). Split that sucker, and boom, you can mock the requires.

If the mocked function relies on a bunch of private state, this still isn't a good reason not to split your module somehow. The very fact that it relies on a bunch of internal state implies, to me, that the concerns of the module are not clearly thought out. Perhaps there is even a third module to split out, that represents some kind of data class or DTO, which can be passed in as an argument.

Also, are you exporting functions just for testing that would otherwise be private? Why is it that external code would call the mocked function directly, but also call the other functions that themselves need to call it? I bet there is some kind of disconnect going on here. Maybe the mocked function needs to stay but all the functions need to be split in half with the half removed going into another module. You get the idea.

When testing gets very hard, it's almost always a sign of refactoring being needed...

No testing tomfoolery required:

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

After reading this thread and testing the proposed solutions, I still can't get this to work. From what I've read some people made this work but I can't figure out how.

Could someone tell me the code I need to add to the following example to make the tests pass?

// 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')
  })
})

Thanks!

I wanted to only mock a single lodash method like lodash.random and was able to do it easily with:

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)
});

Hope that helps :)

Something that worked for our team working with typescript was to create a const we export instead of exporting the function directly.
Not working:
export function doSomething(a, b) {}
Working:
export const doSomething = function (a, b) {}

I changed export like what @arbielsk do, it works! But I dont know what's the difference between two kinds of export...

@dgrcode Did you ever find a solution to your example? As far as I can tell, what you're trying to do is not supported by mocking via Jest. Specifically, I think that mocking in just is basically rewiring imports so that external views of the module see the mocked methods. However, in your example, foo and bar are in the same module and so foo's view of bar can't be mocked out.

I believe that your options are either:
1) Re-organize your code so that foo imports a module that includes bar
2) Use babel-plugin-rewire

Please correct me if I'm misunderstanding!

I had a slightly different requirement: I wanted to mock an entire module _except_ for one function. Using @MajorBreakfast's solution as a starting point, I came up with the following:

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

@dgrcode Did you ever find a solution to your example? As far as I can tell, what you're trying to do is not supported by mocking via Jest. Specifically, I _think_ that mocking in just is basically rewiring imports so that external views of the module see the mocked methods. However, in your example, foo and bar are in the same module and so foo's view of bar can't be mocked out.

I believe that your options are either:

  1. Re-organize your code so that foo imports a module that includes bar
  2. Use babel-plugin-rewire

Please correct me if I'm misunderstanding!

That was actually my case
My understanding of how modules could be mocked was all messed up 🤦‍♂

Basically
If 2 function are in the same module, and calling one another

If foo is calling bar

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

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

Put bar in a new file (to be mocked)

I moved the bar method in a new file, and now I could use many of the examples above
Big thanks to @yoni-abtech in making me understand that 🤣

The way I see it after reading this whole thread and testing over and over, there are 3 options....

Option 1 - declare all functions using const

This requires you to mandate the use of function expressions over declarations. Fortunately, the func-style eslint rule has got your back.

Using export const allows you to spyOn functions that are used by other functions from within the _same_ module.

// 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);
  });
});

Option 2 - Use rewire babel plugin

If you don't want to mandate the use of function expressions (i.e. using const) this could be a good approach.

This allows you to _rewire_ (aka mock) functions from the same module. I could imagine the code would look something like that below but haven't tested it. Also from their docs it looks like you can rewire functions in the same module that are not even exported from the module 👍, imagine the example below without exporting the message function.

Example:

// 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');
  });
});

See the docs for examples.

Note: Requires babel transpilation

Option 3 - Separate all functions into seperate modules/files

This option is the least favorable but would clearly work just fine with the typical mock functionality.


PS: Although this tread was very enlightening and often entertaining, I hope this synopsis mitigates the need for others to read the whole thread. ✌️

Thanks @nickofthyme, you just ended a couple days of banging my head against this.

@nickofthyme your option 1 fails in both my app, and 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 You appear to be correct. Sorry, I should have tested this with CRA.

I got the mocking described in option 1 to work here. Test it using the yarn test:hello script.

Result

> 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.

It requires using a custom jest.config.js file using ts-jest and calling jest --config=./jest.config.js directly, not through react-scripts. I am not sure how jest is configured in the react-scripts but I think there may be a way to update the config somehow.

This fix removes the transforms for *.css and *.svg files, so ignore the App.tsx errors.

Is there anything particular that needs to be done in order to make it work?
I would say I have pretty standard setup (without ts) and it doesn't work out of the box.

I’ll look at it a little more tonight and see if that’s possible.

@danielhusar I looked into last night and couldn't get an out of the box solution. The key is the jest config transformer which CRA allows you to override in package.json#jest. The js and ts files are transpiled using babel-jest but react-scripts blocks you from using a .babelrc config file and setting the test env which they set in react-scripts test here.

I wish I could dig deeper but don't have the time right now.

Hmm Im still bit struggling to make it work (on my custom setup, not the cra).
(latest version of both jest and babel-jest)

This is my jest config:

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

And my 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"]
    }
  }
}

For anyone struggling with option 1, it's important to use () => { return expression } instead of () => (expression) function.

I got option 1 to work by modifying it to:

import * as test from './test';

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

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

Not pretty but it should work.

@nickofthyme, Option 1 is correct as you have it. But if you change the code to:

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

Then it breaks. Presumably because you create a new object literal and export that.

Interesting observation. Thanks @maletor

Jest has a very simple and clear example in their documentation of how to partially mock a module. This works with both ES import and Node require statements.
https://jestjs.io/docs/en/jest-object#jestrequireactualmodulename

@johncmunson That's a good point. However, this example you showed of mocking a module only works if you only need to run jest.mock _once_ and none of the mocked method use another export from that module.

Take the example from above...I've added bar to show how I want to mock the module differently between foo and bar.

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

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

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

Using jest.mock with jest.requireActual I think would go something like this.

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
  });
});

I even tried mocking them separately with jest.doMock and still got the same result.


Click to see code

```ts
import * as testModule from './hello';

describe('test hello', 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.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
});

});
```

The issue with this approach is that requiring the actual module, then say calling foo, still calls the actual message function and not the mock.

I wish it were this simple but from what I see this does not help for the examples from this thread. If I am missing something here please let me know. I'll gladly admit fault.

For anyone who comes across this looking for a solution, the following seems to be working for me when exporting many const/functions in one file, and importing them in a file which I'm testing

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

The following works and is a bit shorter:

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

In Typescript, just import instead of require:

import * as module from './module';

This has the advantage of making life easy to restore original functions and clear mocks.

For anyone who comes across this looking for a solution, the following seems to be working for me when exporting many const/functions in one file, and importing them in a file which I'm testing

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

The following works and is a bit shorter:

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

In Typescript, just import instead of require:

import * as module from './module';

This has the advantage of making life easy to restore original functions and clear mocks.

Oh, yes also this method is not working if your object only has getter defined. The error message could be like below:

Test suite failed to run

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

Probably need to use jest.mock(..) for this case. :bowing_man:

My mocks are working by using the following:

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()
})

It doesn't seem so elegant and doesn't scale so easily, it works very well though and suits my cases for now.. but if anybody can suggest any other syntax that would be great! This is the only syntax that seems to be working.

es6 module code:

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

After transpiled to 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;

There are many ways to solve this situation.

  1. You need to change the code like this, so you can use the mocked/spied 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;

Or,

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

unit test results:

 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. use rewire package to mock funcA
    ...

Besides, you need to take a look at this docs: https://nodejs.org/api/modules.html#modules_exports_shortcut, to see what exactly does require do

The solution in this stackoverflow post worked for me
https://stackoverflow.com/a/53402206/1217998

Basically first you convert all the functions you want to convert to 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');

Then you can use it like normal if you want or mock it like this

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

You can use this to get the original implementation

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

The solution in this stackoverflow post worked for me
https://stackoverflow.com/a/53402206/1217998

Basically first you convert all the functions you want to convert to 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');

Then you can use it like normal if you want or mock it like this

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

You can use this to get the original implementation

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

Thank you!

Jest has a very simple and clear example in their documentation of how to partially mock a module. This works with both ES import and Node require statements.
https://jestjs.io/docs/en/jest-object#jestrequireactualmodulename

Does not work when the mocked function is called from within the module.

Also, I found that sometimes it can be useful to mock function the way that you don't change the original function but call function with some custom (additional) variables:

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

In my case I needed to pass come config to function without which it would use default one.

This is the only way I found to do this, so if you come up with some better or easier ideas, will be glad to hear :)

FWIW I've pulled together various approaches with runnable examples in https://github.com/magicmark/jest-how-do-i-mock-x/blob/master/src/function-in-same-module/README.md

This does not answer the OPs question/issue, but is a solution with some refactoring involved. I've found separating my functions into different files, and then mocking those imports, is the easiest thing to do.

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

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

```js
// babel.config.js

module.exports = {
presets: [
[
'@babel/preset-env',
{
targets: {
node: 'current',
},
},
],
],
};

```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)

I've recently run into this issue. None of the proposed solutions work for me because I'm unable to change the code. The babel-plugin-rewire also does not work for me. Is there any other solutions out there to test that a function was called by another function within the same module? This honestly seems like it should work or that there should be a babel plugin out there that does this. Any help would be much appreciated!

I've recently run into this issue. None of the proposed solutions work for me because I'm unable to change the code. The babel-plugin-rewire also does not work for me. Is there any other solutions out there to test that a function was called by another function within the same module? This honestly seems like it should work or that there should be a babel plugin out there that does this. Any help would be much appreciated!

Have you checked out https://github.com/facebook/jest/issues/936#issuecomment-659597840? There's a minimal repro in there that mocks functions calls in the same file.

Was this page helpful?
0 / 5 - 0 ratings