In this article we will see how you can mock a javascript module with jest.
Before going further we need to understand how module loading works in NodeJS.
When you use require to load a module in Node.js, the module is cached after the first load. This means that every subsequent call to require will return the same object.
Let's take an example. Here is a simple module that exports a message:
```javascript// module.jsmodule.exports = { message: 'hello',}```
When you require this module multiple times:
```javascriptconst module1 = require('./module')const module2 = require('./module')
console.log(module1 === module2) // true```
Both module1 and module2 point to the exact same object in memory. Node.js caches the module in require.cache using the resolved file path as the key.
Knowing this, you can mock a module by directly manipulating require.cache. Here is how:
```javascript// Override the cached modulerequire.cache[require.resolve('./module')] = { exports: { message: 'mocked hello', },}
const mockedModule = require('./module')console.log(mockedModule.message) // 'mocked hello'```
This works because require checks the cache first before loading the module from disk. By replacing the cached entry, all subsequent require calls will return your mock.
Jest provides a cleaner way to mock modules with jest.mock(). Here is how it works:
```javascript// tests/example.test.jsjest.mock('./module', () => ({ message: 'mocked with jest',}))
const module = require('./module')
test('should use mocked module', () => { expect(module.message).toBe('mocked with jest')})```
An important thing to know about jest.mock(): it is hoisted to the top of the file. Even though it appears after the import in the code, Jest moves it to the top before execution. This is why you can use jest.mock() below your import statements and it still works.
For ES module syntax with import, this hoisting behavior is what makes jest.mock() work correctly:
```javascriptimport module from './module'
jest.mock('./module', () => ({ default: { message: 'mocked with jest' }, __esModule: true,}))
test('should use mocked module with ES modules', () => { expect(module.message).toBe('mocked with jest')})```
The __esModule: true flag tells Jest to treat the mock as an ES module, so import module from './module' picks up the default export correctly.
