Jest: How to mock one specific method of a class

Using jest.spyOn() is the proper Jest way of mocking a single method and leaving the rest be. Actually there are two slightly different approaches to this.

1. Modify the method only in a single object

import Person from "./Person";

test('Modify only instance', () => {
    let person = new Person('Lorem', 'Ipsum');
    let spy = jest.spyOn(person, 'sayMyName').mockImplementation(() => 'Hello');

    expect(person.sayMyName()).toBe("Hello");
    expect(person.bla()).toBe("bla");

    // unnecessary in this case, putting it here just to illustrate how to "unmock" a method
    spy.mockRestore();
});

2. Modify the class itself, so that all the instances are affected

import Person from "./Person";

beforeAll(() => {
    jest.spyOn(Person.prototype, 'sayMyName').mockImplementation(() => 'Hello');
});

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

test('Modify class', () => {
    let person = new Person('Lorem', 'Ipsum');
    expect(person.sayMyName()).toBe("Hello");
    expect(person.bla()).toBe("bla");
});

And for the sake of completeness, this is how you'd mock a static method:

jest.spyOn(Person, 'myStaticMethod').mockImplementation(() => 'blah');

Edit 05/03/2021

I see a number of people disagree with the below approach, and that's cool. I do have a slight disagreement with @blade's approach, though, in that it actually doesn't test the class because it's using mockImplementation. If the class changes, the tests will still always pass giving false positives. So here's an example with spyOn.

// person.js
export default class Person {
  constructor(first, last) {
      this.first = first;
      this.last = last;
  }
  sayMyName() {
      return this.first + " " + this.last; // Adjusted to return a value
  }
  bla() {
      return "bla";
  }
}

and the test:

import Person from './'

describe('Person class', () => {
  const person = new Person('Guy', 'Smiley')

  // Spying on the actual methods of the Person class
  jest.spyOn(person, 'sayMyName')
  jest.spyOn(person, 'bla')
  
  it('should return out the first and last name', () => {  
    expect(person.sayMyName()).toEqual('Guy Smiley') // deterministic 
    expect(person.sayMyName).toHaveBeenCalledTimes(1)
  });
  it('should return bla when blah is called', () => {
    expect(person.bla()).toEqual('bla')
    expect(person.bla).toHaveBeenCalledTimes(1)
  })
});

Cheers! 🍻


I don't see how the mocked implementation actually solves anything for you. I think this makes a bit more sense

import Person from "./Person";

describe("Person", () => {
  it("should...", () => {
    const sayMyName = Person.prototype.sayMyName = jest.fn();
    const person = new Person('guy', 'smiley');
    const expected = {
      first: 'guy',
      last: 'smiley'
    }

    person.sayMyName();

    expect(sayMyName).toHaveBeenCalledTimes(1);
    expect(person).toEqual(expected);
  });
});

Have been asking similar question and I think figured out a solution. This should work no matter where Person class instance is actually used.

const Person = require("../Person");

jest.mock("../Person", function () {
    const { default: mockRealPerson } = jest.requireActual('../Person');

    mockRealPerson.prototype.sayMyName = function () {
        return "Hello";
    }    

    return mockRealPerson
});

test('MyTest', () => {
    const person = new Person();
    expect(person.sayMyName()).toBe("Hello");
    expect(person.bla()).toBe("bla");
});