Jest spyOn Not Working Fix
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()inafterEach - 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
- Placing the wildcard pattern first in case expressions, making all subsequent patterns unreachable
- Using
headandtailinstead of pattern matching, causing runtime errors on empty lists - 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
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro