Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I mock an ES6 module in only a single test in Jest?

Say I have the following module:

src/validators.js

export const isPositiveNumber = value => value > 0 && value < Number.MAX_SAFE_INTEGER;

I use it in another module:

src/calculators/volume.js

import { isPositiveNumber } from '../validators';
import { InvalidArgumentException } from '../exceptions';

export const sphere = radius => {
    if (!isPositiveNumber(radius)) {
        throw new InvalidArgumentException('radius must be a positive number');
    }

    return (4/3) * Math.PI * Math.pow(radius, 3);
};

Then in my test:

tests/calculators/volume.test.js

import { volume } from '../../src/calculators/volume';
import { InvalidArgumentException } from '../../src/exceptions';
import { isPositiveNumber } from '../../src/validators';

jest.mock('../../src/validators');

describe('Volume calculations', () => {
    describe('sphere()', () => {
        it('should throw an exception if the radius is invalid', () => {
            isPositiveNumber.mockReturnValue(false);
            expect(() => volume()).toThrow(InvalidArgumentException);
        });

        it('should compute the volume', () => {
            isPositiveNumber.mockReturnValue(true);
            expect(volume(3)).toBeCloseTo(113,3);
        });
    });
});

This works, EXCEPT, I don't want to have mock isPositiveNumber in the second test that actually computes the volume.

I want the isPositiveNumber mock to be ONLY in the validation test.

I do not know how to do this given the ES6 module setup I'm using. It seems to require the mock interception happen outside of the scope of the test, which means I have to mock the return value in EVERY test in the suite.

This is just an example of a simple test, but there will be more complex tests later, and I want to know how to more precisely mock ES6 modules on a per-test basis.

Any help would be appreciated.

like image 997
AgmLauncher Avatar asked Apr 15 '18 06:04

AgmLauncher


1 Answers

That's probably because you're using babel-jest, which according to the documentation, will hoist jest.mock() to top-level regardless of where it's actually placed in your ES6 module code.

When using babel-jest, calls to mock will automatically be hoisted to the top of the code block.

Instead, you can use the jest.doMock() and jest.dontMock() methods in your before() and after() hooks respectively for individual tests that require you to mock the functions in your module definition.

like image 97
Patrick Roberts Avatar answered Oct 31 '22 01:10

Patrick Roberts