Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Jest + MockImplementationOnce + is not working for 2nd time

I am using JEST framework for unit testing for my node.js project. I Used mockImplementationOnce for mocking thirdParty library methods as follows: jest.mock('abc', () => { return { a: { b: jest.fn() } }; }); const abcjs= require('abc');

describe("1st test", () => { 
  test("should return true", async () => {
    abcjs.a.b.mockImplementationOnce(() => Promise.resolve(
      return true}
    ));
  });
});

describe("2nd test", () => { 
  test("should return false", async () => {
    abcjs.a.b.mockImplementationOnce(() => Promise.resolve(
      return false}
    ));
  });
});

1st test executed successfully , but 2nd test Its calling the actual method, its not mocking. I tried resetting mocks afterEach but it didn't help.

like image 400
Subburaj Avatar asked Nov 24 '20 08:11

Subburaj


Video Answer


2 Answers

I was having the same issue yesterday with mocking aws-sdk.

It turned out that after you have mocked a whole module once, you can't override the behaviour of that mock in the same file again.

I am surprised that your first test actually passed, even though your default mock function was simply a jest.fn() without any return value.

There is a whole discussion here on this - https://github.com/facebook/jest/issues/2582

Final Solution from the thread:

// no mocking of the whole module

const abcjs= require('abc');


describe("1st test", () => { 
  test("should return true", async () => {

    // Try using jest.spyOn() instead of jest.fn

    jest.spyOn(abcjs.a,'b').mockImplementationOnce(() => Promise.resolve(true)));
    //expect statement here
  });
});

describe("2nd test", () => { 

jest.restoreAllMocks(); // <----------- remember to add this

  test("should return false", async () => {
    jest.spyOn(abcjs.a,'b').mockImplementationOnce(() => Promise.resolve(false)));
    // expect statement here
  });
});

Basically, DON'T mock the whole module but instead only mock the functions you want from it.

like image 70
nishkaush Avatar answered Oct 21 '22 14:10

nishkaush


Maybe this can be a reference for you. If it's me, then the idea looks like this:

Think of it as the third party library method you are referring to.

/**
 * Module dependencies.
 */
const abcjs = {
    a: { b: () => null }
}

then the test scenario that I imagine is ...

describe('a test scenario', () => {
    afterEach(() => {
        // Restores all mocks back to their original value.
        // only works when the mock was created with jest.spyOn;
        jest.restoreAllMocks()
    })

    describe('1st test', () => {
        test('should return true', async () => {
            jest.spyOn(abcjs.a, 'b').mockImplementation()
            /**
              * Accepts a value that will be returned for one call to the mock function. Can be chained so that
              * successive calls to the mock function return different values. When there are no more
              * `mockResolvedValueOnce` values to use, calls will return a value specified by `mockResolvedValueOnce`. 
              */
            abcjs.a.b.mockResolvedValueOnce(true).mockResolvedValueOnce('isTrue')

            let resultExpected = await abcjs.a.b()
            
            // Ensures that a mock function is called an exact number of times.          
            expect(abcjs.a.b).toHaveBeenCalledTimes(1)
            // Used when you want to check that two objects have the same value.
            expect(resultExpected).toEqual(true)

            // try to invoked the function for second times...
            resultExpected = await abcjs.a.b()

            // Ensures that a mock function is called an exact number of times...
            // which is the second time !
            expect(abcjs.a.b).toHaveBeenCalledTimes(2)
            // Used when you want to check that two objects have the same value.
            expect(resultExpected).toEqual('isTrue')
        })
    })

    describe('2nd test', () => {
        test('should return false', async () => {
            jest.spyOn(abcjs.a, 'b').mockImplementation()
            abcjs.a.b.mockResolvedValueOnce(false)

            const resultExpected = await abcjs.a.b()

            expect(abcjs.a.b).toHaveBeenCalledTimes(1)
            expect(resultExpected).toEqual(false)
        })
    })
})

If it was me, I prefer to return the promise using this mockFn.mockResolvedValue(value) or mockFn.mockRejectedValue(value) see the doc instead of mockImplementation(() => Promise.resolve(value))

This is just one approach to testing that I can describe. Hopefully this will give you an idea.

like image 37
Eka putra Avatar answered Oct 21 '22 14:10

Eka putra