What is the difference between jest.mock(module) and jest.fn()

Birish picture Birish · Dec 11, 2016 · Viewed 7.3k times · Source

I have tried couple different ways of defining the mock function and all of my tries failed. When I try to define it as follow:

jest.mock('../src/data/server', ()=> ({server: {report: jest.fn()}}));
expect(server.report.mock).toBeCalledWith(id, data, () => {...}, () => {...});

I get the this error:

expect(jest.fn())[.not].toBeCalledWith()
jest.fn() value must be a mock function or spy.
Received: undefined

If I define the mock as :

var spy = jest.mock('../src/data/server', ()=> ({server: {report: jest.fn()}}));
expect(spy).toBeCalledWith(id, data, () => {...}, () => {...});

it returns the following error:

    expect(jest.fn())[.not].toBeCalledWith()
    jest.fn() value must be a mock function or spy.
    Received:
      object: {"addMatchers": [Function anonymous], "autoMockOff": [Function anonymous], "autoMockOn": [Function anonymous], "clearAllMocks": [Function anonymous], "clearAllTimers": [Function anonymous], "deepUnmock": [Function anonymous], "disableAutomock": [Function anonymous], "doMock": [Function anonymous], "dontMock": [Function anonymous], "enableAutomock": [Function anonymous], "fn": [Function anonymous], "genMockFn": [Function bound getMockFunction], "genMockFromModule": [Function anonymous], "genMockFunction": [Function bound getMockFunction], "isMockFunction": [Function isMockFunction],
 "mock": [Function anonymous], "resetModuleRegistry": [Function anonymous], "resetModules": [Function anonymous], "runAllImmediates": [Function anonymous], "runAllTicks": [Function anonymous], "runAllTimers": [Function anonymous], "runOnlyPendingTimers": [Function anonymous], "runTimersToTime": [Function anonymous],"setMock": [Function anonymous], "unmock": [Function anonymous], "useFakeTimers": [Function anonymous], "useRealTimers": [Function anonymous]}

As my third try I defined the mock as:

const st = require.requireActual('../src/data/server', ()=> ({server: {report: jest.fn()}}));
st.report = jest.fn();
expect(st.report).toBeCalledWith(id, data,  () => {...}, () => {...});

And I get this error:

expect(jest.fn()).toBeCalledWith(expected)
Expected mock function to have been called with:
  ["1033083fe", {"address": "test address", "affiliation": "testaaa", 
  "city": "testcity", "copyright": true, "country": "testcountry", "email": "[email protected]", "message": "testmessage", 
  "name": "testname", "phone": "1234567890", "zipcode": "12345"}, [Function anonymous], [Function anonymous]]
But it was not called.

I wonder what is the problem and how these 3 ways of defining mock are different?

P.S. the code can be find here: Write a Unit test in Jest for a React form

Answer

Andreas Köberle picture Andreas Köberle · Dec 11, 2016

In the first example you say that when ever the module '../src/data/server' is imported in some other module it will be {server: {report: jest.fn()}}. In the next line you try to use server. The problem is that you never import the module that you mock. Maybe in the module that you test server is {server: {report: jest.fn()}}, but in you in test server is just undefined as you never import it. To fix this also have to import it.

import server from '../src/data/server'
jest.mock('../src/data/server', ()=> ({server: {report: jest.fn()}}));
expect(server.report.mock).toBeCalledWith(id, data, () => {...}, () => {...});

In the second example it seams like jest.mock just returns jest.

The last example fails cause you never call st.report.

Back to the main question. The difference between jest.mock and jest.fn

jest.mock replaces one module with either just jest.fn, when you call it with only the path parameter, or with the returning value of the function you can give it as the second parameter. So in your first example when ever in one of your files you wanna test or in the test itself, '../src/data/server' is imported it will be {server: {report: jest.fn()}}.

Which brings us the jest.fn(). This will return a spy. A spy is a function that can remember each call that was made on it, and with which parameters.

Lets say you have a module that you wanna test:

import server from './data/server'

server.report('test123')

To test that this call was made with the correct parameter you need to mock this dependency to './data/server' with something you can control.

import server from '../src/data/server'
import fileToTest from '../src/fileToTest'
jest.mock('../src/data/server', ()=> ({server: {report: jest.fn()}}));
expect(server.report.mock).toBeCalledWith('test123');

What happens here is that before all the import stuff starts jest replaces '../src/data/server' with your mock implementation, so when fileToTest is import the sever and call it, it actually call the spy function. Which you can then expect that it was called with the correct parameter.

Btw. what you try in your test, checking for function, will not work as the functions you pass when calling the spy and the function you pass into toBeCalledWith are not identical.

In this case I would check for each single parameter

expect(server.report.mock.calls[0][0]).toBe("1033083fe");
expect(server.report.mock.calls[0][0]).toEqual({"address": "test address", "affiliation": "testaaa", 
  "city": "testcity", "copyright": true, "country": "testcountry", "email": "[email protected]", "message": "testmessage", 
  "name": "testname", "phone": "1234567890", "zipcode": "12345"});