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)

Answer from ysfaran on Stack Overflow
🌐
Jest
jestjs.io › the jest object
The Jest Object · Jest
1 week ago - To mock properties that are defined as getters or setters, use jest.spyOn(object, methodName, accessType) instead.
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);
Discussions

Jest.mock vs jest.spyOn
I advise making every attempt to avoid both. You should architect things to allow for dependency injection when possible. More on reddit.com
🌐 r/reactjs
17
8
October 26, 2025
How to use jest.spyOn with @swc/jest?
Historical context from #5059: swc "fixed" a bug that enables an invalid use of jest.spyOn. More on github.com
🌐 github.com
5
6
How do I spyOn third party function with jest?
I am having trouble mocking a third party dependency. I'm always recieving this error: Cannot spy the undefined property because it is not a function; undefined given instead Here are the deta... More on stackoverflow.com
🌐 stackoverflow.com
Jest spyOn a function not Class or Object type
I am familiar with setting spies on Class or Object methods, but what about when the function is just an export default - such that the method itself is independent, like a utility? I have some More on stackoverflow.com
🌐 stackoverflow.com
🌐
Jest
jestjs.io › mock functions
Mock Functions · Jest
1 week ago - jest.spyOn(console, 'log'); const ... expect(jest.mocked(console.log).mock.calls).toHaveLength(1); }); Constructs the type of a spied class or function (i.e....
🌐
Reddit
reddit.com › r/reactjs › jest.mock vs jest.spyon
r/reactjs on Reddit: Jest.mock vs jest.spyOn
October 26, 2025 -

I'm still kind of confused when to uese each implementation. Like i've been looking only and to what I understand is if you want a dummy implementation and don't care about ever getting the original values then use jest.mock. If you want to validate that a function is called then use jest.SpyOn

Would everyone agree with this?

🌐
DEV Community
dev.to › devin-rosario › complete-guide-to-jestspyon-for-unit-testing-4io6
Complete Guide to Jest.spyOn for Unit Testing - DEV Community
October 16, 2025 - SpyOn fails. TypeScript makes this messier. Article from Salto in March 2025 detailed how TypeScript accepts mock as predicate even when implementation returns wrong type. Jest.Mock and jest.fn() typed as function from any to any, so compiler accepts mocks that violate interfaces.
🌐
Echobind
echobind.com › post › how-to-mock-using-jest-spyon-part-2-3
How to Mock Using Jest.spyOn (Part 2)
October 16, 2019 - test('creates Contract on correct date', () => { const NOW = '2019-05-03T08:00:00.000Z'; const mockDateNow = jest .spyOn(global.Date, 'now') .mockImplementation(() => new Date(NOW).getTime()); const mutation = ` mutation createContract { createContract { startedOn } } `; const response = await graphQLRequestAsUser(mutation); const { data } = response.body; expect(data.startedOn).toEqual(NOW); mockDateNow.mockRestore(); });
Find elsewhere
🌐
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 - To create spy, you can use jest.spyOn() with 2 arguments given: the object and the method. Some functionalities that are provided by spy functions are tracking the number of times the funtion has been called, the parameters that is is called with and also the value it returns.
🌐
Silvenon
silvenon.com › blog › mocking-with-jest › functions
Mocking with Jest: Spying on Functions and Changing Implementation
May 22, 2022 - We can’t just replace Math.random with a mock function because we want to preserve its functionality, instead we can spy on it using jest.spyOn, which wraps it in a mock function and returns it so we can track it: const MontyPython = require('./monty-python') describe('MontyPython', () => ...
🌐
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 - Jest spies are instantiated using jest.spyOn(obj, 'functionName'). Note: you can’t spy something that doesn’t exist on the object. jest.toBeCalled() and jest.toHaveBeenCalled() are aliases of each other. expect(stubOrSpy).toBeCalled() passes if the stub/spy is called one or more times.
🌐
CodeSignal
codesignal.com › learn › courses › isolating-dependencies-with-test-doubles-with-typescript-jest › lessons › utilizing-spies-in-test-driven-development-with-jest
Utilizing Spies in Test Driven Development with Jest
Spies are a powerful tool in Jest and other testing frameworks that allow you to watch and record how functions in your application are used without modifying their behavior.
Top answer
1 of 2
5

The example code mixes ES6 import/ export syntax with Node module.exports syntax...

...but based on a library that looks like this:

lib.js

function Viewer() { }

Viewer.prototype.foo = function () { }

module.exports = Viewer;

...it would be used like this:

mp_wrapper.js

import Viewer from './lib';  // <= Babel allows Viewer to be used like an ES6 default export

export const createViewer = container => new Viewer(container);

...and to spy on Viewer you would need to mock the entire library in your test:

mp_wrapper.spec.js

import Viewer from './lib';
import { createViewer } from './mp_wrapper';

jest.mock('./lib', () => jest.fn());  // <= mock the library

test('returns a new instance of the Viewer class', () => {
  const viewer = createViewer('the container');
  expect(Viewer).toHaveBeenCalledWith('the container');  // Success!
  expect(viewer).toBeInstanceOf(Viewer);  // Success!
});

Note that if the library was an ES6 library then you could spy on the default export directly like this:

import * as lib from './lib';

const spy = jest.spyOn(lib, 'default');  // <= spy on the default export

...but because of the way Babel handles the interop between ES6 and non-ES6 code this approach doesn't work if the library is not ES6.


Edit: response to the follow-up question

jest.genMockFromModule generates a mocked version of the module and returns it.

So for example this:

const mock = jest.genMockFromModule('lib');

...generates a mocked version of lib and assigns it to mock. Note that this does not mean that the mock will be returned when lib is required during a test.

jest.genMockFromModule can be useful when creating a manual mock:

__mocks__/lib.js

const lib = jest.genMockFromModule('lib');  // <= generate a mock of the module
lib.someFunc.mockReturnValue('some value');  // <= modify it
module.exports = lib;  // <= export the modified mock

In your final solution you have these two lines:

jest.genMockFromModule('lib');
jest.mock('lib');

This line:

jest.genMockFromModule('lib');

...doesn't actually do anything since it is generating a mock of the module but the returned mock isn't being used for anything.

This line:

jest.mock('lib');

...tells Jest to auto-mock the lib module and is the only line that is needed in this case.

2 of 2
1

Here is a solution:

util.js

const util = {
  isElement() {}
};

module.exports = util;

View.js, the third-party module:

function Viewer() {
  // Doing things
  console.log('new viewer instance');
}

Viewer.prototype.foo = function() {};

module.exports = { Viewer };

my_wrapper.js:

const { Viewer } = require('./viewer');
const util = require('./util');

module.exports = {
  createViewer: container => {
    if (util.isElement(container)) {
      return new Viewer(container);
    } else {
      throw new Error('Invalid Element when attempting to create underlying viewer.');
    }
  }
};

Unit test:

const { Viewer } = require('./viewer');
const my_wrapper = require('./');
const util = require('./util');

jest.mock('./viewer', () => {
  return {
    Viewer: jest.fn()
  };
});

describe('mp_wrapper', () => {
  beforeEach(() => {
    jest.resetAllMocks();
  });
  describe('createViewer', () => {
    it('t1', () => {
      util.isElement = jest.fn().mockReturnValueOnce(true);
      let viewer = my_wrapper.createViewer('el');
      expect(util.isElement).toBeCalledWith('el');
      expect(viewer).toBeInstanceOf(Viewer);
    });

    it('t2', () => {
      util.isElement = jest.fn().mockReturnValueOnce(false);
      expect(() => my_wrapper.createViewer('el')).toThrowError(
        new Error('Invalid Element when attempting to create underlying viewer.')
      );
      expect(Viewer).not.toBeCalled();
    });
  });
});

Unit test result:

PASS  src/stackoverflow/57712713/index.spec.js
  mp_wrapper
    createViewer
      ✓ t1 (6ms)
      ✓ t2 (5ms)

----------|----------|----------|----------|----------|-------------------|
File      |  % Stmts | % Branch |  % Funcs |  % Lines | Uncovered Line #s |
----------|----------|----------|----------|----------|-------------------|
All files |      100 |      100 |       50 |      100 |                   |
 index.js |      100 |      100 |      100 |      100 |                   |
 util.js  |      100 |      100 |        0 |      100 |                   |
----------|----------|----------|----------|----------|-------------------|
Test Suites: 1 passed, 1 total
Tests:       2 passed, 2 total
Snapshots:   0 total
Time:        4.134s, estimated 9s
🌐
Medium
rrish7g.medium.com › jest-fn-vs-jest-spyon-understanding-the-differences-for-effective-testing-0f5928f7a411
Jest.fn() vs Jest.spyOn(): Understanding the Differences for Effective Testing | by Rrish | Medium
November 27, 2023 - Real Behaviour Testing: For integration tests where the actual implementation contributes to the behaviour under test, jest.spyOn() allows you to verify the integration without stubbing out the code.
🌐
Medium
ayodolani.medium.com › jest-react-testing-library-2fe14a810991
JEST: React Testing Library. SPY on a class constructor using Jest | by AYO AKINDOLANI | Medium
April 18, 2023 - const cognitoUserSpy = jest .spyOn(CognitoClasses, 'CognitoUser') .mockReturnValue({ setAuthenticationFlowType: () => {}, authenticateUser: () => {} }); // write your expect here // expect() // restore/clear all mocks
🌐
Chakshunyu
chakshunyu.com › blog › how-to-spy-on-a-named-import-in-jest
How To Spy On An Exported Function In Jest | A technical blog by Chak Shun Yu
September 3, 2021 - The spyOn function is one of the most powerful utility functions in Jest. It allows you to spy on a function, observe interactions, and mock them accordingly.
🌐
Meticulous
meticulous.ai › blog › how-to-use-jest-spyon
How to use Jest spyOn with React.js and Fetch
Another way to supplant dependencies is with use of Spies. Spies record some information depending on how they are called. Jest’s spyOn method is used to spy on a method call on an object.
🌐
Developright
developright.co.uk › posts › mocking-functions-or-methods-with-jest.html
Mocking & Spying with Jest SpyOn | DevelopRight.co.uk
December 30, 2022 - // import package import randomLibrary from './libraries/random'; // inside test (shortened for brevity) jest.spyOn(randomLibrary, 'functionOrMethod');
🌐
Zirkelc
zirkelc.dev › posts › jest-spy-on-classes
How To Spy on Classes
June 28, 2023 - class Person { static hello() { return 'Hello, I am a static method.'; } goodbye() { return 'Goodbye, I am an instance method.'; } } test('should call static method', () => { // Spy on the static method const spy = jest.spyOn(Person, 'hello'); // Invoke the static method Person.hello(); // Test if the static method has been called expect(spy).toHaveBeenCalled(); });
🌐
CodeSandbox
codesandbox.io › s › jest-spyon-vs-mocking-pdyvb5
jest - spyOn vs mocking - CodeSandbox
June 13, 2022 - jest - spyOn vs mocking by ainneo using babel-core, babel-jest, babel-preset-es2015, babel-preset-react, jest, react, react-addons-test-utils, react-dom, react-test-renderer
Published   Apr 27, 2022
Author   ainneo