41

let's say I have a module that needs to be initialized once in the start of the app (to pass on configuration). module will look something like this :

MyModule.js

let isInitiazlied;

const myModule = {

    init: function() {
        isInitiazlied = true;
    },
    do: function() {
        if (!isInitiazlied)
            throw "error"
        //DO THINGS
    }
}

export default myModule;

I want to unittest it, using jest. test file looks something like this :

MyModule.test.js

import myModule from './MyModule'

describe('MyModule', () => {
    describe('init', () => {
        it('not throws exception when called', () => {
            expect(() => myModule.init()).not.toThrow();
        });
    })
    describe('do', () => {
        it('throw when not init', () => {
            expect(() => myModule.do()).toThrow();
        });
    })
})

when I run the test, the 2nd test fail, as the module already initialized so the exception is not thrown. I tried using jest.resetModules() in beforeEach, but that didn't work.

Is there a way to solve it (different module pattern/test case) ?

Amico
  • 653
  • 2
  • 6
  • 15

3 Answers3

61

You have to re-import or re-require your module. Check the doc or this issue for more information:

https://github.com/facebook/jest/issues/3236

https://facebook.github.io/jest/docs/en/jest-object.html#jestresetmodules

describe('MyModule', () => {
    beforeEach(() => {
        jest.resetModules()
    });

    describe('init', () => {
        const myModule = require('./MyModule');

        it('not throws exception when called', () => {
            expect(() => myModule.init()).not.toThrow();
        });
    })
    describe('do', () => {
        const myModule = require('./MyModule');

        it('throw when not init', () => {
            expect(() => myModule.do()).toThrow();
        });
    })
})
ltamajs
  • 1,231
  • 11
  • 15
  • 2
    works ! thanks. the only thing missing was adding **.default** to the require statement, as MyModule exported as default – Amico Feb 26 '18 at 14:24
  • Thanks so much for this! I created a little helper function to help take care of resetting env vars between/after tests and providing a friendly API for doing this: https://gist.github.com/DesignByOnyx/8384f190ba363d5003f73578c7e36285 – Ryan Wheale Jul 08 '22 at 15:10
  • this should not be the correct accepted answer, require oddly did not work, even if it is in the jest docs – George Tiganila Nov 08 '22 at 16:06
18

@ltamajs solution is great for require but in the case you are using import then you will receive the next error.

SyntaxError: /path/to/test/file.js: 'import' and 'export' may only appear at the top level

To solve this issue, you can use the babel-plugin-dynamic-import-node plugin and then reset the modules. Overall, it looks like this:

describe('MyTests', () => {
  let MyModule;

  beforeEach(() => {
    return import('../module/path').then(module => {
      MyModule = module;
      jest.resetModules();
    });
  });

  test('should test my module', () => {
    expect(MyModule.aMethod).not.toBeUndefined();
  });
});

Source: https://github.com/facebook/jest/issues/3236#issuecomment-698271251

Lucio
  • 4,753
  • 3
  • 48
  • 77
  • 3
    This works great for me even w/o the babel plugin, thanks! – pir Sep 26 '21 at 18:24
  • 6
    You can even keep `MyModule` typed if you write it as `let MyModule: typeof import('../module/path')` – George Nov 02 '21 at 01:38
  • 1
    I don't see how this can possibly work, your beforeEach is returning a promise which you are not awaiting – Michael Sherris Caley Jan 21 '22 at 16:38
  • 2
    @MichaelSherrisCaley "`beforeEach` ... can handle asynchronous code [by] returning a promise" - see https://jestjs.io/docs/setup-teardown#repeating-setup-for-many-tests – Lucio Jan 24 '22 at 16:37
  • @Lucio shouldn't the anonymous function be async, i.e. `beforeEach(async () => { ... })` for it to be awaited? Similar to how Jest docs recommends using the `async` keyword for async tests: https://jestjs.io/docs/asynchronous#asyncawait – pir Mar 02 '22 at 21:24
  • Nevermind. I tested this by putting a long sleep function and beforeEach awaits completing execution regardless of the await keyword. – pir Mar 02 '22 at 21:35
0

In my case only re-requiring didn't work. I got through jest documentation and found this. This suggest use of jest.resetModules(). This basically just resets the cache and on re-requiring module it makes sure it gets loaded from scratch. So when ever you want are re-requiring use this to make sure the it is getting loaded from scratch

Anant Jain
  • 21
  • 2