Jest mock comparison

jest.spyOn() vs jest.fn(): which one should you use?

Both can track calls and control mock behavior, but they start from different situations. Compare them side by side and choose the right Jest mock for your test.

jest.spyOn()

Watches a method that already exists and calls the original implementation by default.

VS

jest.fn()

Creates a new mock function with no original implementation unless you provide one.

The main difference in one minute

Use jest.spyOn() when the method already exists and you want Jest to observe or temporarily change that real method.

Use jest.fn() when you need to create a new mock function, callback, dependency or placeholder yourself.

Choose jest.spyOn() if...

You already have an object method and want to watch what happens when your code calls it.

Choose jest.fn() if...

You need a mock function that does not need to wrap an existing method.

Choose based on what already exists

The easiest way to decide is to look at the function you are trying to test.

Use jest.spyOn()

  • The method already exists on an object.
  • You want to track calls to the real method.
  • You may want the original implementation to run.
  • You may temporarily mock the method and restore it.

Use jest.fn()

  • You need to create a mock from scratch.
  • You are mocking a callback.
  • No real implementation needs to run.
  • You want to inject a fake function into your code.

jest.spyOn() vs jest.fn() comparison

The important differences become clearer when you compare their behavior directly.

Feature
jest.spyOn()
jest.fn()
Requires an existing method
Yes
No
Tracks calls
Yes
Yes
Tracks arguments
Yes
Yes
Original code runs by default
Yes
No
Can use mockReturnValue()
Yes
Yes
Can use mockImplementation()
Yes
Yes
Can restore an original method
Yes
Not automatically
Best for callbacks
Usually no
Yes

See the difference in real code

These examples perform similar assertions, but the mock is created in a different way.

Existing method jest.spyOn()
const service = {
  send(message) {
    return message;
  }
};

const spy = jest.spyOn(
  service,
  'send'
);

service.send('hello');

expect(spy)
  .toHaveBeenCalledWith('hello');
New mock function jest.fn()
const send = jest.fn();

send('hello');

expect(send)
  .toHaveBeenCalledWith(
    'hello'
  );
Why spyOn fits here

service.send() already exists, so the spy watches that real method rather than creating a separate function.

Why jest.fn fits here

No function existed yet. The test creates a new mock function and calls it directly.

Which one should you use in these cases?

Match your testing situation with the tool that fits it best.

Watch an existing API method

The service method already exists and your application calls it.

jest.spyOn()
Create a callback mock

You need a fake callback to pass into another function.

jest.fn()
Verify a real method was called

You want to observe an existing object's behavior.

jest.spyOn()
Inject a fake dependency

Your code expects a function and you can supply the dependency yourself.

jest.fn()
Mock temporarily, then restore

You want the real object method back after the test.

jest.spyOn()
Build a mock implementation from scratch

There is no real object method that needs to be preserved.

jest.fn()
A simple rule to remember

If the function already exists and belongs to an object, jest.spyOn() is usually the clearer starting point. If you need to create the function yourself, start with jest.fn().

jest.spyOn() vs jest.fn() questions

Quick answers to the differences developers most often need to understand.

jest.spyOn() wraps a method that already exists. jest.fn() creates a new mock function from scratch.

Yes. By default the original implementation runs. You can replace the behavior by adding a mock return value or mock implementation.

Usually yes. A callback often does not need an existing object method, so creating it directly with jest.fn() is simpler.

A standalone jest.fn() does not have an original object method to restore. A spy can restore the method it wrapped.