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!