Home > Mobile >  Jest mocks bleeding between tests, reset isn't fixing it
Jest mocks bleeding between tests, reset isn't fixing it

Time:10-14

Testing two modules, helper which makes use of render. It's possible for render to throw, so I handle that in helper, and I want tests to ensure that's working as expected.

When I originally wrote the tests, I wrote what was needed for that test in the test itself, including mocks, using jest.doMock. Once all the tests pass I wanted to refactor to share mocks where possible.

So this code works great:

test('throws', async () => {
    jest.doMock('./render', () => jest.fn(async () => { throw new Error('mock error'); }));

    const helper = require('./helper');

    expect(async () => { helper(); }).rejects.toThrow('mock error');
    expect(log_bug).toHaveBeenCalled();
});

test('succeeds', async () => {
    jest.doMock('./render', () => jest.fn(async () => 'rendered result'));

    const helper = require('./helper');

    expect(await helper()).toEqual(true); //helper uses rendered result but doesn't return it
    expect(log_bug).not.toHaveBeenCalled();
});

HOWEVER, these are not the only two tests and by far most of the other tests that mock render want it to return its success state. I tried to refactor that success use-case out to a file in __mocks__/render.js like so:

// __mocks__/render.js
module.exports = jest.fn(async () => 'rendered result');

And then refactor my tests to this, to be more DRY:

//intention: shared reusable "success" mock for render module
jest.mock('./render');

beforeEach(() => {
    jest.resetModules();
    jest.resetAllMocks();
});

test('throws', async () => {
    //intention: overwrite the "good" render mock with one that throws
    jest.doMock('./render', () => jest.fn(async () => { throw new Error('mock error'); }));

    const helper = require('./helper');

    expect(async () => { await helper(); }).rejects.toThrow('mock error');
    expect(log_bug).toHaveBeenCalled();
});

test('succeeds', async () => {
    //intention: go back to using the "good" render mock
    const helper = require('./helper');
    expect(await helper()).toEqual(true); //helper uses rendered result but doesn't return it
    expect(log_bug).not.toHaveBeenCalled();
});

With this updated test code, the error-logging test still works as expected -- the mock is overwritten to cause it to throw -- but then for the next test, the error is thrown again.

If I reverse the order of these tests so that the mock overwriting is last, then the failure doesn't happen, but that is clearly not the correct answer.

What am I doing wrong? Why can't I get my mock to properly reset after overriding it with doMock? The doMock docs do kind of illustrate what I'm trying to do, but they don't show mixing it with normal manual mocks.

CodePudding user response:

Aha! I kept digging around and found this somewhat similar Q A, which led me to try this approach instead of using jest.doMock to override inside of a test:

//for this one test, overwrite the default mock to throw instead of succeed
const render = require('./render');
render.mockImplementation(async () => {
    throw new Error('mock error');
});

And with this, the tests pass no matter what order they run!

  • Related