Search code examples
node.jsjestjsmockingfsspy

How to Mock `fs.promises.writeFile` with Jest


I am trying to mock the promise version of fs.writeFile using Jest, and the mocked function is not being called.

Function to be tested (createFile.js):

const { writeFile } = require("fs").promises;

const createNewFile = async () => {
    await writeFile(`${__dirname}/newFile.txt`, "Test content");
};

module.exports = {
    createNewFile,
};

Jest Test (createFile.test.js):

const fs = require("fs").promises;
const { createNewFile } = require("./createFile.js");

it("Calls writeFile", async () => {
    const writeFileSpy = jest.spyOn(fs, "writeFile");

    await createNewFile();
    expect(writeFileSpy).toHaveBeenCalledTimes(1);

    writeFileSpy.mockClear();
});

I know that writeFile is actually being called because I ran node -e "require(\"./createFile.js\").createNewFile()" and the file was created.

Dependency Versions

  • Node.js: 14.1.0
  • Jest: 26.6.3

-- Here is another attempt at the createFile.test.js file --

const fs = require("fs");
const { createNewFile } = require("./createFile.js");

it("Calls writeFile", async () => {
    const writeFileMock = jest.fn();

    jest.mock("fs", () => ({
        promises: {
            writeFile: writeFileMock,
        },
    }));

    await createNewFile();
    expect(writeFileMock).toHaveBeenCalledTimes(1);
});

This throws the following error:

ReferenceError: /Users/danlevy/Desktop/test/src/createFile.test.js: The module factory of `jest.mock()` is not allowed to reference any out-of-scope variables.
    Invalid variable access: writeFileMock

Solution

  • Since writeFile is destructured at import time instead of being consistently referred as fs.promises.writeFile method, it cannot be affected with spyOn.

    It should be mocked as any other module:

    jest.mock("fs", () => ({
      promises: {
        writeFile: jest.fn().mockResolvedValue(),
        readFile: jest.fn().mockResolvedValue(),
      },
    }));
    
    const fs = require("fs");
    
    ...
    
    await createNewFile();
    expect(fs.promises.writeFile).toHaveBeenCalledTimes(1);
    

    It make sense to mock fs scarcely because unmocked functions provide side effects and potentially have negative impact on test environment.