joke
is a typesafe, boilerplate free version of jest.mock
.
- Less boilerplate than
jest.mock
. - Type-safe imports. No more type-casting.
- TS/JS Language Server recognizes them when moving files around.
- Supports partial mocking (
mockSome
).
npm install --saveDev @userlike/joke @userlike/babel-plugin-joke
yarn add -D @userlike/joke @userlike/babel-plugin-joke
Add @userlike/babel-plugin-joke
to your babel plugins.
If you use ts-jest
, please additionally refer to the "Usage with ts-jest
" section below.
import { mock } from "@userlike/joke";
const { fetchUser } = mock(import("./service"));
fetchUser.mockReturnValue(Promise.resolve({ id: 1, name: "Jane Doe" }));
Auto-mock the whole module.
import { mock } from "@userlike/joke";
const { fetchUser } = mock(import("./service"));
fetchUser.mockReturnValue(Promise.resolve({ id: 1, name: "Jane Doe" }));
Use the second argument of mock
to provide a partial implementation. Behind the scenes, it extends auto-mocked module with the given implementation using Object.assign
.
import { mock } from "@userlike/joke";
const { fetchUser } = mock(import("./service"), () => ({
fetchUser: () => Promise.resolve({ id: 1, name: "Jane Doe" })
}));
When you need to mock a module partially and want to keep the rest of the module unmocked, you can use mockSome
. Behind the scenes, it uses jest.requireActual
by extending its actual implementation with the given implementation using Object.assign
.
import { mockSome } from "@userlike/joke";
import { renderUser } from "./my-component";
// fetchUser is mocked, getUserId is the real implementation
const { fetchUser, getUserId } = mockSome(import("./service"), () => ({
fetchUser: jest.fn()
}));
test(async () => {
const user = { id: 1, name: "Jane Doe" };
fetchUser.mockReturnValue(Promise.resolve(user));
await renderUser();
expect(document.getElementById("#user-id").innerText).toBe(getUserId(user));
});
When you want to skip auto-mocking, you can use mockAll
. It's equivalent to jest.mock(module, moduleFactory)
.
import { mockAll } from "@userlike/joke";
import { renderUser } from "./my-component";
const { fetchUser } = mockAll(import("./service"), () => ({
fetchUser: jest.fn()
}));
test(async () => {
const user = { id: 1, name: "Jane Doe" };
fetchUser.mockReturnValue(Promise.resolve(user));
await renderUser();
expect(document.getElementById("#user-id").innerText).toBe(getUserId(user));
});
If you use ts-jest
instead of Babel, you need to additionally ensure each of the following:
- That Babel preprocessing is enabled in your
ts-jest
configuration section. - That Babel is configured to use
joke
as a plugin. - That the
module
key oftsconfig.json
'scompilerOptions
is set to at leastes2020
, oresnext
to support dynamic imports. You may also need to setmoduleResolution
tonode
for the generalimport
syntax to work properly. - That the code is transpiled down to the JS syntax
jest
understands (you may use@babel/preset-env
for that purpose).
Note: if you don't want to modify your main tsconfig.json
file, you can introduce a separate configuration named e.g. tsconfig.tests.json
.
Example Typescript configuration for tests:
{
"extends": "tsconfig.json",
"compilerOptions": {
"module": "ES2020",
"moduleResolution": "node"
}
}
To enable Babel preprocessing in ts-jest
, as well as to configure the tsconfig
file you want use for tests, add or update the globals
section in your jest config.
Example with separate Babel and Typescript configuration files:
"globals": {
'ts-jest': {
"babelConfig": "true",
"tsConfig": "tsconfig.test.json"
}
}
Example with inline Typescript and Babel configuration:
"globals": {
'ts-jest': {
"babelConfig": {
"plugins": ["@userlike/babel-plugin-joke"],
"presets": [
[
"@babel/preset-env",
{
"targets": {
"node": 'current'
}
}
]
]
},
"tsConfig": {
"module": "es2020",
"moduleResolution": "node",
}
}
}
For details, see ts-jest
configuration docs.