Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

[jest]: TypeError: Object prototype may only be an Object or null: undefined

While writing jest tests, I needed to use memfs as a mock for the Nodejs native file system module, so I used jest's manual mocks, but I'm getting this error:

> rimraf tests/{coverage,public} && jest

 PASS  tests/x.test.ts (19.926 s)
 FAIL  tests/mix.test.ts
  ● Test suite failed to run

    TypeError: Object prototype may only be an Object or null: undefined
        at Function.setPrototypeOf (<anonymous>)

      at node_modules/graceful-fs/polyfills.js:139:39
      at patch (node_modules/graceful-fs/polyfills.js:141:5)
      at patch (node_modules/graceful-fs/graceful-fs.js:104:3)
      at Object.<anonymous> (node_modules/graceful-fs/graceful-fs.js:96:18)
      at Object.<anonymous> (node_modules/fs-extra/lib/fs/index.js:5:12)

 PASS  tests/options.test.ts (35.412 s)

Test Suites: 1 failed, 2 passed, 3 total
Tests:       6 passed, 6 total
Snapshots:   0 total
Time:        36.834 s
Ran all test suites.

Here are the files for a minimal reproduction of the error:

// src/index.ts
// this is just a minimal reproduction
import "laravel-mix";
import fs from "fs";

export default function getContent(path: string) {
    return fs.readFileSync(path, "utf-8");
}
// tests/index.test.ts

import path from "path";
import fs from "fs";
import memfs from "memfs";
import getContent from "../src";

// Use memfs instead of native fs module.
jest.mock("fs");
jest.mock("fs/promises");

beforeAll(() => {
  memfs.fs.mkdirSync(path.resolve(), {recursive: true});
});

// this is for demonstration only.
test("should mock fs", () => {
    expect(fs).toBe(memfs.fs);
});

test("returns content from memfs", () => {
    memfs.fs.writeFileSync("test.txt", "test text");

    const result = getContent("test.txt");

    expect(result).toBe("test text");
});

// more tests
// jest.config.js

module.exports = {
    collectCoverageFrom: ["src/*.ts"],
    coverageDirectory: "tests/coverage",
    preset: "ts-jest",
    testEnvironment: "node",
    testMatch: ["<rootDir>/tests/**/*.test.ts"],
};
// tsconfig.json

{
  "compilerOptions": {
    "downlevelIteration": true,
    "declaration": true,
    "declarationDir": "dist",
    "strict": true,
    "esModuleInterop": true,
    "resolveJsonModule": true,
    "moduleResolution": "node",
    "sourceMap": true,
    "noImplicitReturns": true,
    "noImplicitOverride": true,
    "importHelpers": true
  }
}
// package.json

{
  "name": "jest-mock-error-reproduction",
  "version": "1.0.0",
  "description": "",
  "main": "src/index.ts",
  "scripts": {
    "test": "jest"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@types/jest": "^27.0.1",
    "@types/node": "^16.7.8",
    "@types/serve-static": "^1.13.10",
    "jest": "^27.1.0",
    "laravel-mix": "^6.0.29",
    "memfs": "^3.2.4",
    "ts-jest": "^27.0.5",
    "typescript": "~4.2.0"
  }
}

And the manual mock files:

// __mocks__/fs.ts

import {fs} from "memfs";

export default fs;
// __mocks__/fs/promises.ts

import {fs} from "memfs";

export default fs.promises;

Note that when I remove :

jest.mock("fs");
jest.mock("fs/promises");

from tests/index.test.ts, tests fail as expected.

I tried debugging the source code that produces the error, and I couldn't find the problem.

I also tried to use import * as memfs from "memfs" syntax in the __mock__ files, because from other answers it seems like that solves the problem, but the error persists.

Any help would be appreciated.

like image 332
soufiane yakoubi Avatar asked Sep 05 '25 07:09

soufiane yakoubi


1 Answers

I was able to narrow the source of the problem down to a dependency.

It seems like memfs is not compatible with graceful-fs which is a dependency of fs-extra which in turn is a dependency of a library I'm using in my own code (laravel-mix). So here's a very minimal reproduction of the error now:

// tests/index.test.ts

import fs from "fs";
import memfs from "memfs";
import "graceful-fs"; // comment this and the test should pass.

jest.mock("fs");
jest.mock("fs/promises");

test("should mock fs", () => {
    expect(fs).toBe(memfs.fs);
});

To solve my issue, I changed the virtual file system library in use. I switched from memfs to mock-fs.

First, I installed mock-fs and @types/mock-fs:

npm i -D mock-fs @types/mock-fs

And then I used it in tests/index.test.ts:

// tests/index.test.ts

import path from "path";
import fs from "fs";
import mock_fs from "mock-fs";
import getContent from "../src";

beforeEach(() => {
    mock_fs();
});

afterEach(() => {
    mock_fs.restore();
});

test("returns content from mocked fs", () => {
    fs.writeFileSync("test.txt", "test text");

    const result = getContent("test.txt");

    expect(result).toBe("test text");
});

// more tests
like image 146
soufiane yakoubi Avatar answered Sep 07 '25 20:09

soufiane yakoubi