Library / SDK
marchaos/jest-mock-extended avatar
marchaos/jest-mock-extended

jest-mock-extended gives Jest mocks a typed calledWith chain

Type safe mocking extensions for Jest https://www.npmjs.com/package/jest-mock-extended

908 stars68 forksTypeScriptMIT

At a glance

What is it?
jest-mock-extended is a small TypeScript library that builds Jest mocks from your interface types, so a mock is a compile time checked double with argument specific expectations. It removes the casts that hand written jest.fn() stubs need, and it depends on Jest and TypeScript through peer dependency ranges you should check before upgrading.
Who is it for?
Adopt jest-mock-extended when your tests are written in TypeScript and mock interfaces rather than concrete classes, because the mock is derived from the same type the production code uses, so a signature change breaks the test at compile time instead of at assertion time. Do not adopt it for a JavaScript codebase, since the value of the library is entirely in the types, and do not expect it to model behaviour, because it produces doubles with no logic.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 30 days ago.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on November 7, 2026, and from our analysis. They are not legal advice.

From daily snapshots

Star history

  • marchaos/jest-mock-extended908 ★ · +2
Star history
Repository2026-10-212026-11-07
marchaos/jest-mock-extended906908

2 Stars in the last 17 days, measured from the daily GitHub snapshot. Compare with other projects

Editorial analysis

A mock built from your interface type, not from a cast

Jest gives you jest.fn(), and in a TypeScript project the awkward part starts when you assign that function to a slot typed by your own interface. The usual workarounds are a cast, a partial, or a hand written stub object, and each of them moves the type information away from the place where it is most useful. jest-mock-extended takes a different approach. The library describes itself as type safe mocking extensions for Jest, and its feature list leads with complete TypeScript type safety for interfaces, argument types and return types, plus the ability to mock any interface or object. The result is that the mock is derived from the type rather than attached to it by assertion. A method that exists on the interface is available on the mock, and the return value you set is checked against the declared return type. For a team whose tests are the main consumer of their own interfaces, that inversion is the whole argument for the dependency.

mock() returns a proxy whose members behave like Jest mocks

The entry point is the mock function, imported from the package and called with a type parameter. The documentation's example declares a PartyProvider interface with a getPartyType method, a getSongs method and a start method, then creates the mock and calls a method on it:

ts
import { mock } from 'jest-mock-extended';
ts
const provider = mock<PartyProvider>();
provider.getSongs.calledWith('disco party').mockReturnValue(['Dance the night away', 'Stayin Alive']);
expect(provider.getSongs('disco party')).toEqual(['Dance the night away', 'Stayin Alive']);

The mechanics matter here. Calling the method does not require you to stub anything first, and the expectation is recorded the way Jest records it, so the standard matchers such as toHaveBeenCalledWith work without adaptation. The feature list describes the API as a familiar Jest like API, and that familiarity is the point: a developer who already knows jest.fn() needs no new mental model, only better types. The documentation also notes that calledWith() is an extension that provides argument specific expectations, which works for objects and for functions, and that the argument types are type checked even when you use matchers.

calledWith() chains matchers onto specific arguments

The calledWith() extension is where the library earns its name, because it turns invocation matching into a typed chain rather than a separate assertion. The pattern is to state the arguments first and configure the return value second. Matchers are supported in the chain:

ts
provider.getSongs.calledWith(any()).mockReturnValue(['Saw her standing there']);
provider.getSongs.calledWith(anyString()).mockReturnValue(['Saw her standing there']);

The matcher list is broad and deliberately compatible with Jasmine matchers, which the feature list calls an extensive matcher API. The documented set covers any(), anyBoolean(), anyString(), anyNumber(), anyFunction(), anyObject(), anyArray(), anyMap() and anySet(), plus isA(class) for a class check, includes('value') for array membership, containsKey and containsValue for objects, has for sets, and the notNull() and notUndefined() checks. Functions are not left out. The mockFn() helper creates a jest.fn() that carries the same calledWith extension, which is what you want when the unit under test accepts a callback rather than an object:

ts
type MyFn = (x: number, y: number) => Promise<string>;
const fn = mockFn<MyFn>();
fn.calledWith(1, 2).mockReturnValue('str');

The arguments here are checked against the function type, so a call with the wrong arity or the wrong argument type is a compile error rather than a runtime surprise.

mockDeep() reaches into objects that methods return

Mocking a method that returns an object is where plain mocks get tedious, because you have to construct the returned object yourself. The library answers with mockDeep(), described in the documentation as a replacement for mock when your class has objects returned from methods that you would also like to mock. The deep mock is typed as DeepMockProxy, and nested members are themselves mocks:

ts
const mockObj: DeepMockProxy<Test1> = mockDeep<Test1>();
mockObj.deepProp.getNumber.calledWith(1).mockReturnValue(4);
expect(mockObj.deepProp.getNumber(1)).toBe(4);

The same example shows the one refinement available. When the nested value is a function rather than an object, the deep mock needs to be told, and the option that does it is funcPropSupport:

ts
const mockObj: DeepMockProxy<Test1> = mockDeep<Test1>({ funcPropSupport: true });
mockObj.deepProp.calledWith(1).mockReturnValue(3)

This is a real trade-off rather than a free feature. A deep proxy is convenient because every nested call is available without setup, but it also means an unstubbed nested call returns undefined rather than failing, so a test can pass for the wrong reason unless you configure the fallback.

fallbackMockImplementation makes a forgotten stub fail the test

The documentation shows a second option that pairs well with the deep mock, a fallback mock implementation used when no return value has been defined with calledWith. The point is to convert a silent undefined into a loud failure:

ts
const mockObj = mockDeep<Test1>({
    fallbackMockImplementation: () => {
        throw new Error('please add expected return value using calledWith');
    },
});
expect(() => mockObj.getNumber()).toThrowError('not mocked');

The same option is available on the plain mock factory, and the documentation uses it to show what happens when a test forgets to specify a return value, with a fallback that throws an Error carrying the message not mocked. If you take one habit from this library, take that one. A mock that returns undefined for every unstubbed call makes the assertion about the value you care about pass while hiding the calls you forgot, and a thrown fallback surfaces the gap at the point of the call. Clearing is handled in the same spirit, since the library exposes a mockClear and a mockReset with the same functionality as jest.fn(), and the documented usage is to call mockReset(mock), or mockClear(mock), in a beforeEach block so each test starts from a known state.

Installing the library and what it pulls in

The package is published to npm and is meant to be a development dependency, since it exists only for tests. The installation section gives two equivalent commands:

bash
npm install jest-mock-extended --save-dev
bash
yarn add jest-mock-extended --dev

The package manifest explains the rest. The published entry point is main lib/index.js with type declarations at lib/index.d.ts, and the files array restricts what is published to lib, so the TypeScript sources in src do not ship. Runtime dependencies are small: fast-deep-equal and ts-essentials. Everything else is a dev dependency, including jest, ts-jest, typescript, prettier, rimraf and coveralls, with scripts for build (tsc), test (jest) and coveralls. Because the library is built with tsc, it produces declaration files, which is what makes the typed API available to consumers rather than only inside the project. A first useful run is to create a mock of a single interface in one test file, assert one call, and run the suite, so that the type checking and the runtime behaviour are both confirmed before the pattern spreads through the codebase.

Peer dependency ranges are the upgrade cost

The licence field on the repository says MIT, and the package manifest declares the same licence, so there is no obligation attached to using it. The cost of this dependency shows up in the peerDependencies rather than in the licence. The library expects @jest/globals at ^28.0.0 || ^29.0.0 || ^30.0.0, jest across a range from ^24.0.0 up to ^30.0.0, and typescript from ^3.0.0 up to ^6.0.0. That range is generous, and it is also the thing to check before you bump anything. A project on a Jest major outside that list will get a peer dependency warning, and a TypeScript version outside the range may see type errors in your test files, because the library's own types are resolved by your compiler. The maintenance picture is calm: the repository is not archived, the last push was on 2026-10-08, and the release history shows 4.0.2 on 2026-10-08, 4.0.1 on 2026-04-20 and 4.0.0 on 2025-06-14. A major version bump is the one to read the changelog for, since a change to the exported types reaches into every test file at once.

Where a proxy mock is the wrong choice

Two situations argue against it. The first is a JavaScript codebase, where the entire value of the library is the type information and the proxy simply gives you jest.fn() behaviour with a little extra syntax. The second is a test that needs realistic behaviour rather than a double, such as a repository whose return value has to satisfy an invariant, or a component that reads several properties of an object in a specific order. A proxy mock can only report what you told it to report, so an over specified stub drifts from the real implementation and the test passes while production breaks. The alternative approaches are worth naming. A hand written stub object typed against the same interface is more verbose but makes the fake explicit and is easy to review. A partial mock, where only the methods under test are replaced and the rest delegate to a real instance, keeps behaviour honest and is the better choice when the class has meaningful logic. Where a hand written stub, a partial mock and this library overlap, the deciding factor is type checking: in a TypeScript project the proxy wins on that, and in a JavaScript project it wins on nothing.

Editorial conclusion

Adopt jest-mock-extended when your tests are written in TypeScript and mock interfaces rather than concrete classes, because the mock is derived from the same type the production code uses, so a signature change breaks the test at compile time instead of at assertion time. Do not adopt it for a JavaScript codebase, since the value of the library is entirely in the types, and do not expect it to model behaviour, because it produces doubles with no logic. Before you install it, compare the peer dependency ranges in package.json against the Jest and TypeScript versions your project already uses, since the library supports Jest from 24 to 30 and TypeScript from 3 to 6, and set a fallbackMockImplementation so that an unstubbed call fails loudly instead of returning undefined.

Frequently asked questions

How do I install jest-mock-extended?

Install it as a development dependency with npm install jest-mock-extended --save-dev, or with yarn add jest-mock-extended --dev. It is published to npm with type declarations at lib/index.d.ts.

What is the difference between mock and mockDeep?

mock creates a mock from an interface or object type, while mockDeep creates a deep mock whose nested objects and functions are also mocks. mockDeep takes a funcPropSupport option when the nested value is a function rather than an object.

How do calledWith and matchers work in jest-mock-extended?

calledWith takes the expected arguments and returns the mock so you can set a return value for that specific invocation, and the argument types are checked even when a matcher is used. The matcher list follows Jasmine conventions, including any(), anyString(), includes, containsKey, containsValue, has, isA, notNull and notUndefined.

How do I clear or reset mocks created with jest-mock-extended?

The library exposes a mockClear and a mockReset with the same functionality as jest.fn(). The documented usage is to call mockReset(mock), or mockClear(mock), inside a beforeEach block.

Which Jest and TypeScript versions does jest-mock-extended support?

The peer dependencies allow @jest/globals from 28 to 30, jest from 24 to 30, and typescript from 3 to 6. A Jest major or TypeScript major outside those ranges will produce a peer dependency warning or type errors in your test files.

Official sources

  1. Issues
  2. License: MIT
  3. marchaos/jest-mock-extended on GitHub
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/marchaos-jest-mock-extended.svg)](https://hysenlabs.com/projects/marchaos-jest-mock-extended)

Star history chart for your README

markdown
[![Star history](https://hysenlabs.com/chart/marchaos-jest-mock-extended.svg)](https://hysenlabs.com/projects/marchaos-jest-mock-extended)

Was this useful?