The jest mocking only works on imported functions. In your apiMiddleware.js the default function is calling callApi variable, not the "exported" callApi function. To make the mock work, move callApi into its own module, and import it in apiMiddleware.js

Good question!

Answer from Herman Starikov on Stack Overflow
🌐
GitHub
github.com › jestjs › jest › issues › 6972
Jest spyOn() calls the actual function instead of the mocked · Issue #6972 · jestjs/jest
September 13, 2018 - import * as apiMiddleware from './apiMiddleware'; const { CALL_API, default: middleware, callApi } = apiMiddleware; describe('Api Middleware', () => { const store = {getState: jest.fn()}; const next = jest.fn(); let action; beforeEach(() => { // clear the result of the previous calls next.mockClear(); // action that trigger apiMiddleware action = { [CALL_API]: { // list of properties that change from test to test } }; }); it('calls mocked version of `callApi', () => { const callApi = jest.spyOn(apiMiddleware, 'callApi').mockReturnValue(Promise.resolve()); // error point: middleware() calls the actual `callApi()` middleware(store)(next)(action); // assertion }); }); Please ignore the action's properties and argument of callApi function.
Author   jestjs
Discussions

Jest - when using spyOn function ensure the spied one is not called
7 Jest spyOn() calls the actual function instead of the mocked More on stackoverflow.com
🌐 stackoverflow.com
jestjs - jest.spyOn not calling mocked implementation but rather actual function instead - Stack Overflow
I'm trying to write a unit test for a function that calls some helper functions in the same file. I'm using jest.spyOn to mock away those helper functions as it appears that it can do that. myModul... More on stackoverflow.com
🌐 stackoverflow.com
August 22, 2022
Jest spyOn.mockImplementation calls actual method
I assume that app is bult outside of your tests, and that verifyUser is part of your middleware chain, am I correct ? I might be mistaken, but in your case, jest won't override your implementation (linked to app) even when using spyOn. It will override the module when using mock though. More on stackoverflow.com
🌐 stackoverflow.com
reactjs - What is the difference between jest.fn() and jest.spyOn() methods in jest? - Stack Overflow
I am writing the Unit test cases for my react project and using jest and enzyme for writing test cases. I have read the jest documentation https://jestjs.io/docs/en/jest-object.html#jestspyonobject- More on stackoverflow.com
🌐 stackoverflow.com
Top answer
1 of 2
1

If you want to override the createNewGameWithInitialPlayer and return what you want then, you have to mock the import of GameFactory class in your test.

// Here you are creating your mock and saying the default return Game object
const mockCreateNewGameWithInitialPlayer = jest.fn().mockImplementation(() => new Game());

// Here you say to jest that any file who wants to import "GameFactory"
// will import this fake class
jest.mock('rootofGameFactory/GameFactory', () => ({
    GameFactory: function(){
      return {
        // and when any file wants to execute this method, will execute my mock
        createNewGameWithInitialPlayer: mockCreateNewGameWithInitialPlayer
      }
    }
}))

describe('Testing Game Service', () => {
    test('createRoom', () => {
        const response: RoomResponse = gameService.createRoom({
            name: 'Player 1',
            device: DevicesEnum.ios,
            socketID: 'some-socket-id'
        })
    ...
    }
    ...
}

If you want to change the return object of your mocked method, you have to do it like this...

test('createRoom 2', () => {
        //Here you say to jest, just ONCE (for this test) return an instance of Game2
        mockCreateNewGameWithInitialPlayer.mockImplementationOnce(() => new Game2())
        const response: RoomResponse = gameService.createRoom({
            name: 'Player 1',
            device: DevicesEnum.ios,
            socketID: 'some-socket-id'
        })
    ...
    }
2 of 2
0

The jest documentation mentions

By default, jest.spyOn also calls the spied method. This is different behavior from most other test libraries. If you want to overwrite the original function, you can use jest.spyOn(object, methodName).mockImplementation(() => customImplementation) or jest.replaceProperty(object, methodName, jest.fn(() => customImplementation));

So you could do something like

jest.spyOn(gameFactory, 'GameFactory').mockImplementation(() => { return undefined })
🌐
Lightrun
lightrun.com › answers › facebook-jest-jest-spyon-calls-the-actual-function-instead-of-the-mocked
Jest spyOn() calls the actual function instead of the mocked
By default jest.spyOn() does not override the implementation (this is the opposite of jasmine.spyOn). If you don’t want it to call through you have to mock the implementation: const callApi = jest.spyOn(apiMiddleware, 'callApi').mockImple...
🌐
Stack Overflow
stackoverflow.com › questions › 73450625 › jest-spyon-not-calling-mocked-implementation-but-rather-actual-function-instead
jestjs - jest.spyOn not calling mocked implementation but rather actual function instead - Stack Overflow
August 22, 2022 - However, the jest.spyOn function does not appear to be mocking the implementation of the spied on functions but the test is calling the original functions instead which results in an output of mjordan basketball instead.
🌐
Meticulous
meticulous.ai › blog › how-to-use-jest-spyon
How to use Jest spyOn with React.js and Fetch
Using the above method to spy on a function of an object, Jest will only listen to the calls and the parameters but the original implementation will be executed as we saw from the text execution screenshot. This is the compelling reason to use spyOn over mock where the real implementation still needs to be called in the tests but the calls and parameters have to be validated.
Find elsewhere
Top answer
1 of 3
120

My simple understanding of these two functions in react/frontend projects is the following:

jest.fn()

  • You want to mock a function and really don't care about the original implementation of that function (it will be overridden by jest.fn())
  • Often you just mock the return value
  • This is very helpful if you want to remove dependencies to the backend (e.g. when calling backend API) or third party libraries in your tests
  • It is also extremely helpful if you want to make real unit tests. You don't care about if certain function that gets called by the unit you test is working properly, because thats not part of it's responsibility.

jest.spyOn()

  • The original implementation of the function is relevant for your test, but:
    • You want to add your own implementation just for a specific scenario and then reset it again via mockRestore() (if you just use a jest.spyOn() without mocking it further it will still call the original function by default)
    • You just want to see if the function was called
    • ...
  • I think this is especially helpful for integration tests, but not only for them!

(Good blog post: https://medium.com/@rickhanlonii/understanding-jest-mocks-f0046c68e53c)

2 of 3
55

To my understanding the only difference is that YOU CAN RESTORE ORIGINAL FUNCTION with jest.spyOn and you can't with jest.fn.

Imagine we have some hook that calls a function when component is rendered, here we can just check the function was called, we do not test that function.

Another case if we want original function to test how it works. And we need both in one test file.

Real method:

myMethod() {
  return 33;
}

With jest.fn()

const myMethod = jest.fn().mockImplementation(() => 25);
const result = myMethod();
expect(result).toBe(25);

In case we want to test now real myMethod, we can't restore it back to normal with jest.fn().

Another thing with spies:

const spy_myMethod = jest.spyOn(component, "myMethod").mockImplementation(() => 25);
const result = myMethod();
expect(result).toBe(25);

And now if we want the original myMethod

spy_myMethod.mockRestore();
const result = myMethod();
expect(result).toBe(33);
🌐
Medium
rrish7g.medium.com › demystifying-jest-functions-mock-spyon-and-fn-a312fafb46b9
Demystifying Jest Functions: Mock, SpyOn, and Fn | by Rrish | Medium
November 26, 2023 - Call Through: By default, jest.spyOn will call through to the method's original implementation. This means the method keeps its actual behaviour, but now you also have a record of its usage.
🌐
Medium
medium.com › @catherineangelr › testing-with-spy-and-mock-in-jest-a-beginners-guide-7a25f87010c2
Testing with Spy, Mock, and Stub in Jest: A Beginner’s Guide | by Catherine Angel | Medium
May 21, 2024 - Summary: Stubs are similar to mocks but focus on providing predefined responses to function calls. They simulate specific conditions or scenarios without executing the actual function logic.
🌐
Silvenon
silvenon.com › blog › mocking-with-jest › functions
Mocking with Jest: Spying on Functions and Changing Implementation
May 22, 2022 - But this test is silly, we already know that the function will be called with 42 because we called it within the test itself. We usually want to pass mock functions to the implementation code that we’re testing, then we expect that code to call them in a certain way.
Top answer
1 of 4
102

You were almost done without any changes besides how you spyOn. When you use the spy, you have two options: spyOn the App.prototype, or component component.instance().


const spy = jest.spyOn(Class.prototype, "method")

The order of attaching the spy on the class prototype and rendering (shallow rendering) your instance is important.

const spy = jest.spyOn(App.prototype, "myClickFn");
const instance = shallow(<App />);

The App.prototype bit on the first line there are what you needed to make things work. A JavaScript class doesn't have any of its methods until you instantiate it with new MyClass(), or you dip into the MyClass.prototype. For your particular question, you just needed to spy on the App.prototype method myClickFn.


jest.spyOn(component.instance(), "method")

const component = shallow(<App />);
const spy = jest.spyOn(component.instance(), "myClickFn");

This method requires a shallow/render/mount instance of a React.Component to be available. Essentially spyOn is just looking for something to hijack and shove into a jest.fn(). It could be:

A plain object:

const obj = {a: x => (true)};
const spy = jest.spyOn(obj, "a");

A class:

class Foo {
    bar() {}
}

const nope = jest.spyOn(Foo, "bar");
// THROWS ERROR. Foo has no "bar" method.
// Only an instance of Foo has "bar".
const fooSpy = jest.spyOn(Foo.prototype, "bar");
// Any call to "bar" will trigger this spy; prototype or instance

const fooInstance = new Foo();
const fooInstanceSpy = jest.spyOn(fooInstance, "bar");
// Any call fooInstance makes to "bar" will trigger this spy.

Or a React.Component instance:

const component = shallow(<App />);
/*
component.instance()
-> {myClickFn: f(), render: f(), ...etc}
*/
const spy = jest.spyOn(component.instance(), "myClickFn");

Or a React.Component.prototype:

/*
App.prototype
-> {myClickFn: f(), render: f(), ...etc}
*/
const spy = jest.spyOn(App.prototype, "myClickFn");
// Any call to "myClickFn" from any instance of App will trigger this spy.

I've used and seen both methods. When I have a beforeEach() or beforeAll() block, I might go with the first approach. If I just need a quick spy, I'll use the second. Just mind the order of attaching the spy.


EDIT: If you want to check the side effects of your myClickFn you can just invoke it in a separate test.

const app = shallow(<App />);
app.instance().myClickFn()
/*
Now assert your function does what it is supposed to do...
eg.
expect(app.state("foo")).toEqual("bar");
*/

EDIT: Here is an example of using a functional component. Keep in mind that any methods scoped within your functional component are not available for spying. You would be spying on function props passed into your functional component and testing the invocation of those. This example explores the use of jest.fn() as opposed to jest.spyOn, both of which share the mock function API. While it does not answer the original question, it still provides insight on other techniques that could suit cases indirectly related to the question.

function Component({ myClickFn, items }) {
   const handleClick = (id) => {
       return () => myClickFn(id);
   };
   return (<>
       {items.map(({id, name}) => (
           <div key={id} onClick={handleClick(id)}>{name}</div>
       ))}
   </>);
}

const props = { myClickFn: jest.fn(), items: [/*...{id, name}*/] };
const component = render(<Component {...props} />);
// Do stuff to fire a click event
expect(props.myClickFn).toHaveBeenCalledWith(/*whatever*/);

If a functional component is niladic (no props or arguments) then you can use Jest to spy on any effects you expect from the click method:

import { myAction } from 'src/myActions'
function MyComponent() {
    const dispatch = useDispatch()
    const handleClick = (e) => dispatch(myAction('foobar'))
    return <button onClick={handleClick}>do it</button>
}

// Testing:
const { myAction } = require('src/myActions') // Grab effect actions or whatever file handles the effects.
jest.mock('src/myActions') // Mock the import

// Do the click
expect(myAction).toHaveBeenCalledWith('foobar')
2 of 4
31

You're almost there. Although I agree with @Alex Young answer about using props for that, you simply need a reference to the instance before trying to spy on the method.

describe('my sweet test', () => {
 it('clicks it', () => {
    const app = shallow(<App />)
    const instance = app.instance()
    const spy = jest.spyOn(instance, 'myClickFunc')

    instance.forceUpdate();    

    const p = app.find('.App-intro')
    p.simulate('click')
    expect(spy).toHaveBeenCalled()
 })
})

Docs: http://airbnb.io/enzyme/docs/api/ShallowWrapper/instance.html

🌐
DhiWise
dhiwise.com › post › grow-testing-efficiency-with-jest-spyon-for-default-exports
A Deep Dive into Jest spyOn for Default Exports
April 30, 2025 - The main difference between a mock function and a spyOn function in Jest is that mocking completely replaces the function with a fake version, while spying wraps the actual function, allowing you to observe its behavior without changing its ...
🌐
Code with Hugo
codewithhugo.com › jest-fn-spyon-stub-mock
Jest .fn() and .spyOn() spy/stub/mock assertion reference · Code with Hugo
November 5, 2019 - For example an increment function being called once vs twice is very different. let count = 0; const counter = { increment() { count += 1; }, getCount() { return count; } }; const app = counter => { counter.increment(); }; test('app() with mock counter .toHaveBeenCalledTimes(1)', () => { const mockCounter = { increment: jest.fn() }; app(mockCounter); expect(mockCounter.increment).toHaveBeenCalledTimes(1); }); test('app() with jest.spyOn(counter) .toHaveBeenCalledTimes(1)', () => { const incrementSpy = jest.spyOn(counter, 'increment'); app(counter); expect(incrementSpy).toHaveBeenCalledTimes(1); });
🌐
DEV Community
dev.to › qmenoret › mocks-and-spies-with-jest-32gf
Mocks and Spies with Jest - DEV Community
December 31, 2020 - mockReset - reseting a mock returns the mock to a fresh state, just like if you just called spyOn on the function. All mocked implementation or return value will be forgotten. Of course it also implies everything mockClear implies. mockRestore - Restoring the function actually removes the mock, and restore the original implementation.
🌐
Echobind
echobind.com › post › how-to-mock-using-jest-spyon-part-2-3
How to Mock Using Jest.spyOn (Part 2)
October 16, 2019 - import video from './video'; test('plays video', () => { const spy = jest.spyOn(video, 'play'); video.play(); expect(spy).toHaveBeenCalledTimes(1); }); view raw · Any call to video.play, either in this test file or (if the function is being called as a side-effect) in some other file, will be tracked within this test('plays video', () =>{}) function. If you want to mock out an implementation and overwrite the original function, you can chain .mockImplementation to the end of jest.spyOn:
🌐
Chakshunyu
chakshunyu.com › blog › how-to-mock-only-one-function-from-a-module-in-jest
How To Mock Only One Function From A Module In Jest | A technical blog by Chak Shun Yu
April 18, 2021 - However, from a technical perspective there is quite a difference because jest.spyOn(moduleApi, 'functionToMock') on its own will still run the actual functionToMock code rather than mocking it. Spying a function from a module will only keep track of its calls.