How to Mock Lodash Dependencies in Jest
This article provides a practical guide on mocking Lodash methods and
internal dependencies when running unit tests with Jest. It explains how
to target specific Lodash utilities using Jest's mocking APIs, maintain
the functionality of non-mocked methods with
jest.requireActual, and navigate common issues related to
module resolution and individual submodule imports.
Understanding Lodash Import Styles
How you mock Lodash depends directly on how the library is imported in your application code. Lodash supports three common import patterns:
- Full default import:
import _ from 'lodash'; - Named imports:
import { debounce, cloneDeep } from 'lodash'; - Per-method subpath imports:
import debounce from 'lodash/debounce';
Because Jest intercepts modules based on their module specifier, your mock definition must match the exact import path used by the implementation.
Mocking Specific Methods While Preserving Others
In most testing scenarios, you only want to mock a single Lodash
method (such as an asynchronous timer wrapper like debounce
or a randomizer like uniqueId) while preserving core
data-manipulation helpers like get or map.
Use jest.mock() combined with
jest.requireActual to mock specific functions without
breaking the rest of the library:
// userSearch.js
import { debounce } from 'lodash';
export const triggerSearch = debounce((query, callback) => {
callback(query);
}, 300);// userSearch.test.js
import { triggerSearch } from './userSearch';
import * as lodash from 'lodash';
jest.mock('lodash', () => {
const originalLodash = jest.requireActual('lodash');
return {
__esModule: true,
...originalLodash,
// Replace debounce to execute immediately instead of waiting
debounce: jest.fn((fn) => fn),
};
});
describe('triggerSearch', () => {
it('executes the callback without debouncing delay', () => {
const callback = jest.fn();
triggerSearch('react', callback);
expect(callback).toHaveBeenCalledWith('react');
});
});Mocking Per-Method Subpath Imports
When working with bundle-size optimizations, codebases often import
utilities directly from subpaths such as lodash/debounce.
In this case, mocking 'lodash' will not intercept the call.
You must mock the direct path:
// utils.js
import throttle from 'lodash/throttle';
export function setupScrollListener(fn) {
return throttle(fn, 100);
}// utils.test.js
import { setupScrollListener } from './utils';
jest.mock('lodash/throttle', () => {
return jest.fn((fn) => fn);
});
describe('setupScrollListener', () => {
it('uses the mocked throttle implementation', () => {
const scrollHandler = jest.fn();
const wrapped = setupScrollListener(scrollHandler);
wrapped();
expect(scrollHandler).toHaveBeenCalledTimes(1);
});
});Scoped Mocking with
jest.spyOn
If a mock is only needed for a single test case,
jest.spyOn() is preferred over module-wide mocks because it
makes cleanup straightforward with mockRestore().
import _ from 'lodash';
import { generateReportId } from './report';
describe('generateReportId', () => {
afterEach(() => {
jest.restoreAllMocks();
});
it('generates an ID using a predictable uniqueId value', () => {
const spy = jest.spyOn(_, 'uniqueId').mockReturnValue('fixed_123');
const result = generateReportId();
expect(result).toBe('report_fixed_123');
expect(spy).toHaveBeenCalled();
});
});Mocking Lodash Chaining and Internal Dependencies
When functions utilize Lodash's chaining syntax
(_(data).map(...).value()), mocking individual methods via
simple object assignment fails because Lodash wraps values in internal
wrapper prototypes (lodash.prototype).
To mock chained calls:
- Mock the top-level wrapper function.
- Return a chainable object where each method returns
this, andvalue()returns the intended output.
import _ from 'lodash';
jest.mock('lodash', () => {
const original = jest.requireActual('lodash');
const mockChain = {
map: jest.fn().mockReturnThis(),
filter: jest.fn().mockReturnThis(),
value: jest.fn().mockReturnValue(['mocked', 'result']),
};
const lodashWrapper = jest.fn(() => mockChain);
Object.assign(lodashWrapper, original);
return lodashWrapper;
});For deeply integrated internal helpers that Lodash does not export
directly, avoid attempting to mock internal filenames (such as
lodash/_baseClone). Instead, mock the public Lodash API
methods that invoke those internal functions to keep tests decoupled
from internal implementation details.