Skip to content

Jest SpyOn Not Working Fix

DodaTech Updated 2026-06-24 2 min read

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

Your jest.spyOn() doesn't track calls — the spy shows 0 invocations even though the method definitely ran during the test.

The Problem

// WRONG — spying after the method is called
const calculator = {
  add: (a, b) => a + b,
};

calculator.add(2, 3);
const spy = jest.spyOn(calculator, 'add');

expect(spy).toHaveBeenCalled(); // FAILS
expect(jest.fn()).toHaveBeenCalled()
Expected number of calls: 1
Received number of calls: 0

The spy was created after the method was already called. jest.spyOn() only tracks calls made after the spy is installed.

Step-by-Step Fix

1. Install the spy before calling the method

// RIGHT — spy before invocation
const calculator = {
  add: (a, b) => a + b,
};

test('calls add method', () => {
  const spy = jest.spyOn(calculator, 'add');
  calculator.add(2, 3);
  expect(spy).toHaveBeenCalledWith(2, 3);
  spy.mockRestore();
});

2. Spy on class prototype methods

// RIGHT — spy on prototype
class Logger {
  log(message) {
    console.log(message);
  }
}

test('logs message', () => {
  const spy = jest.spyOn(Logger.prototype, 'log');
  const logger = new Logger();
  logger.log('test');
  expect(spy).toHaveBeenCalledWith('test');
  spy.mockRestore();
});

3. Mock the implementation while spying

// RIGHT — spy with mock implementation
const db = { query: (sql) => `Result of ${sql}` };

test('mocks database query', () => {
  const spy = jest.spyOn(db, 'query').mockReturnValue('Mock result');
  const result = db.query('SELECT * FROM users');
  expect(result).toBe('Mock result');
  expect(spy).toHaveBeenCalled();
  spy.mockRestore();
});

4. Restore spies in afterEach

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

test('first test', () => {
  const spy = jest.spyOn(console, 'log');
  console.log('hello');
  expect(spy).toHaveBeenCalled();
});

Expected output:

PASS  tests/spy.test.js
  ✓ calls add method (6 ms)
  ✓ logs message (4 ms)
  ✓ mocks database query (5 ms)

Prevention Tips

  • Create spies before the method is called
  • Use jest.spyOn() instead of manual mock assignments
  • Always call mockRestore() or use jest.restoreAllMocks() in afterEach
  • Spy on prototype for class instance methods
  • Combine with .mockImplementation() to replace behavior

Common Mistakes with mock spy

  1. Mixing let bindings with <- bindings in do notation, producing type errors
  2. Overlapping type class instances that cause GHC to reject the program with ambiguous dispatch errors
  3. Non-exhaustive pattern matches that compile with warnings then crash at runtime

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

### What's the difference between jest.spyOn and jest.fn?

jest.spyOn() wraps an existing method and tracks calls without changing its behavior. jest.fn() creates a brand new mock function. Use spyOn when you want to monitor calls to a real method, and jest.fn() when you need a standalone mock.

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

The property you're trying to spy on doesn't exist on the object. Make sure the method exists before calling jest.spyOn(). For class methods, use jest.spyOn(ClassName.prototype, 'methodName').

How do I spy on a getter or setter?

Use jest.spyOn(object, 'property', 'get') or jest.spyOn(object, 'property', 'set') as the third argument. This tells Jest to spy on the getter or setter accessor instead of a regular method.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro