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-jestconfiguration section. - That Babel is configured to use
jokeas a plugin. - That the
modulekey oftsconfig.json'scompilerOptionsis set to at leastes2020, oresnextto support dynamic imports. You may also need to setmoduleResolutiontonodefor the generalimportsyntax to work properly. - That the code is transpiled down to the JS syntax
jestunderstands (you may use@babel/preset-envfor 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.