I just started a new React/Next.js project and realized that I cannot mock memoized React components the way I could in a previous project. And I fail to find out why. Here’s a minimal example of my setup:
// /src/components/Foo.tsx
import Bar from "./Bar";
const Foo = () => (
<div>
<Bar />
</div>
);
export default Foo;
// /src/components/Bar.tsx
import { memo } from "react";
const Bar = memo(function Bar_() {
return <div>Original</div>;
});
export default Bar;
Now I want to test Foo
mocking Bar
.
This is what I did in my last project:
// /src/components/Foo.test.tsx
import "@testing-library/jest-dom";
import { render, screen } from "@testing-library/react";
import Foo from "./Foo";
const barModule = require("./Bar");
jest.replaceProperty(barModule, "default", () => <div>Mocked</div>);
describe("Foo", () => {
it("should mock Bar", () => {
render(<Foo />);
expect(screen.queryByText("Original")).not.toBeInTheDocument();
});
});
But when I run the tests, I get the error message "Property default
is not declared configurable". How can I fix this?
Here are my config files:
// jest.config.js
const nextJest = require("next/jest");
/** @type {import('jest').Config} */
const createJestConfig = nextJest({
dir: "./",
});
/** @type {import('jest').Config} */
const config = {
clearMocks: true,
collectCoverage: true,
coverageDirectory: "coverage",
coverageProvider: "v8",
testEnvironment: "jsdom",
};
module.exports = createJestConfig(config);
// tsconfig.json
{
"compilerOptions": {
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true,
"plugins": [
{
"name": "next"
}
],
"paths": {
"@/*": ["./src/*"]
}
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"exclude": ["node_modules"]
}
// package.json
{
"name": "my-app",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint",
"test": "jest"
},
"dependencies": {
"next": "14.1.4",
"react": "^18",
"react-dom": "^18"
},
"devDependencies": {
"@testing-library/jest-dom": "^6.4.2",
"@testing-library/react": "^14.2.2",
"@types/jest": "^29.5.12",
"@types/node": "^20",
"@types/react": "^18",
"@types/react-dom": "^18",
"autoprefixer": "^10.0.1",
"eslint": "^8",
"eslint-config-next": "^14.1.4",
"jest": "^29.7.0",
"jest-environment-jsdom": "^29.7.0",
"postcss": "^8",
"tailwindcss": "^3.3.0",
"typescript": "^5"
}
}
2
Answers
Mock like this:
Since ES modules have a static structure and are not designed to allow their exports to be modified at runtime, trying to mock them directly with methods like
jest.replaceProperty
can lead to errors, especially with the error message "Property default is not declared configurable" which you’re encountering. This behavior is because the exports of ES modules are sealed and cannot be reconfigured, which includes making them non-configurable or non-writable, as required by the Jest mocking mechanism.Use
jest.mock
with Module Factory Parameter