Jest: How to mock one specific method of a class

后端 未结 7 1865
暗喜
暗喜 2020-11-30 23:43

Let\'s suppose I have the following class:

export default class Person {
    constructor(first, last) {
        this.first = first;
        this.last = last         


        
相关标签:
7条回答
  • 2020-12-01 00:21

    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);
      });
    });
    
    0 讨论(0)
  • 2020-12-01 00:25

    Not really answer the question, but I want to show a use case where you want to mock a dependent class to verify another class.

    For example: Foo depends on Bar. Internally Foo created an instance of Bar. You want to mock Bar for testing Foo.

    Bar class

    class Bar {
      public runBar(): string {
        return 'Real bar';
      }
    }
    
    export default Bar;
    

    Foo class

    import Bar from './Bar';
    
    class Foo {
      private bar: Bar;
    
      constructor() {
        this.bar = new Bar();
      }
    
      public runFoo(): string {
        return 'real foo : ' + this.bar.runBar();
      }
    }
    
    export default Foo;
    
    
    

    The test:

    import Foo from './Foo';
    import Bar from './Bar';
    
    jest.mock('./Bar');
    
    describe('Foo', () => {
      it('should return correct foo', () => {
        // As Bar is already mocked,
        // we just need to cast it to jest.Mock (for TypeScript) and mock whatever you want
        (Bar.prototype.runBar as jest.Mock).mockReturnValue('Mocked bar');
        const foo = new Foo();
        expect(foo.runFoo()).toBe('real foo : Mocked bar');
      });
    });
    
    
    

    See also jest.requireActual(moduleName)

    0 讨论(0)
  • 2020-12-01 00:36

    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');
    
    0 讨论(0)
  • 2020-12-01 00:36

    If you are using Typescript, you can do the following:

    Person.prototype.sayMyName = jest.fn().mockImplementationOnce(async () => 
            await 'my name is dev'
    );
    

    And in your test, you can do something like this:

    const person = new Person();
    const res = await person.sayMyName();
    expect(res).toEqual('my name is dev');
    

    Hope this helps someone!

    0 讨论(0)
  • 2020-12-01 00:38

    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");
    });
    
    0 讨论(0)
  • 2020-12-01 00:44

    rather than mocking the class you could extend it like this:

    class MockedPerson extends Person {
      sayMyName () {
        return 'Hello'
      }
    }
    // and then
    let person = new MockedPerson();
    
    0 讨论(0)
提交回复
热议问题