Skip to content

Jest spyOn Not Working Fix

DodaTech Updated 2026-06-24 3 min read

In this tutorial, you'll learn about Jest spyOn Not Working Fix. We cover key concepts, practical examples, and best practices.

You call jest.spyOn(obj, 'method') but the spy doesn't record calls — the method still runs its real implementation and the mock never fires.

The Problem

// WRONG — spying on an instance after it's created
class Logger {
  log(msg) { return `LOG: ${msg}`; }
}

const logger = new Logger();
const spy = jest.spyOn(logger, 'log');

logger.log('test');
expect(spy).toHaveBeenCalled();
expect(jest.fn()).toHaveBeenCalled()
Expected number of calls: 1
Received number of calls: 0

The spy replaced log on the instance, but the reference logger.log works correctly. The issue is often that the code under test holds a different reference to the method.

Step-by-Step Fix

1. Spy on the prototype, not the instance

// RIGHT — spy on the prototype
jest.spyOn(Logger.prototype, 'log');

const logger = new Logger();
logger.log('test');

expect(Logger.prototype.log).toHaveBeenCalledWith('test');

Spying on Logger.prototype.log catches calls on any instance, including those created after the spy.

2. Mock the module when the method is imported directly

// userService.js
export const sendEmail = (to, subject) => {
  return EmailAPI.send(to, subject);
};

// test.js
jest.mock('./emailService', () => ({
  sendEmail: jest.fn()
}));

import { sendEmail } from './emailService';

test('sends email', () => {
  sendEmail('a@b.com', 'Hello');
  expect(sendEmail).toHaveBeenCalled();
});

3. Spy on an object with mockImplementation

// RIGHT — mock implementation replaces the real method
const calculator = {
  add: (a, b) => a + b
};

const spy = jest.spyOn(calculator, 'add').mockImplementation((a, b) => 42);

expect(calculator.add(1, 2)).toBe(42);
expect(spy).toHaveBeenCalledWith(1, 2);

mockImplementation is useful when you need the spy to return a controlled value while still tracking calls.

4. Restore spies after each test

afterEach(() => {
  jest.restoreAllMocks();
});

Without restoration, spies with mockImplementation persist across tests and can cause unexpected behavior.

Expected output:

PASS  tests/logger.test.js
  ✓ spies on prototype method (10 ms)
  ✓ uses mock implementation (6 ms)

Prevention Tips

  • Spy on prototype methods, not instance methods
  • Call jest.restoreAllMocks() in afterEach
  • Use jest.mock() for imported module functions
  • Verify the spy target exists before calling spyOn
  • Check that the code under test references the same object you spied on

Common Mistakes with spyon not working

  1. Placing the wildcard pattern first in case expressions, making all subsequent patterns unreachable
  2. Using head and tail instead of pattern matching, causing runtime errors on empty lists
  3. Forgetting that lazy evaluation defers computation until the value is forced, causing space leaks with unevaluated thunks

These mistakes appear frequently in real-world JEST code. DodaTech's contributors have identified these patterns through analysis of open-source projects and production systems.

Practice Exercise

Write a pure function that safely divides two integers using Maybe, then test it with edge cases like division by zero and negative numbers.

This exercise reinforces the concepts covered in this guide. Try implementing it before checking online solutions.

FAQ

### Why does spyOn fail with "Cannot spy on undefined"?

The property you're trying to spy on doesn't exist on the object. Either the method name is misspelled, the method is defined on the prototype and you're spying on the instance, or the module hasn't loaded yet. Use jest.spyOn(Prototype.prototype, 'methodName') for class methods.

What's the difference between spyOn and fn?

spyOn wraps an existing method and can track calls, restore the original, or replace the implementation. fn creates a brand new mock function with no real implementation. Use spyOn when you need the real behavior with call tracking, use fn for standalone mocks.

How do I spyOn a default export function?

Mock the whole module with jest.mock('./module') and provide a mock implementation. You can also use jest.spyOn on the default import: import * as Module from './module'; jest.spyOn(Module, 'default');.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro