如何用Jest模拟`fs.proness.writeFile`



我正在尝试使用Jest模拟fs.writeFilepromise版本,但没有调用模拟函数。

待测试功能(createFile.js):

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

Jest测试(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();
});

我知道writeFile实际上是被调用的,因为我运行了node -e "require("./createFile.js").createNewFile()"并且创建了文件。

依赖版本

  • Node.js:14.1.0
  • 耶稣:26.6.3

这是对createFile.test.js文件的另一次尝试--

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);
});

这会引发以下错误:

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

由于writeFile在导入时被销毁,而不是一直被称为fs.promises.writeFile方法,因此它不会受到spyOn的影响。

它应该被嘲笑为任何其他模块:

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

嘲笑fs几乎没有意义,因为未被锁定的函数会产生副作用,并可能对测试环境产生负面影响。

模型"fs/承诺";jost中的异步函数

这里有一个使用fs.readir((的简单示例,但它也适用于任何其他异步fs/promise函数。

files.service.test.js

import fs from "fs/promises";
import FileService from "./files.service";
jest.mock("fs/promises");
describe("FileService", () => {
var fileService: FileService;
beforeEach(() => {
// Create a brand new FileService before running each test
fileService = new FileService();
// Reset mocks
jest.resetAllMocks();
});
describe("getJsonFiles", () => {
it("throws an error if reading the directory fails", async () => {
// Mock the rejection error
fs.readdir = jest.fn().mockRejectedValueOnce(new Error("mock error"));
// Call the function to get the promise
const promise = fileService.getJsonFiles({ folderPath: "mockPath", logActions: false });
expect(fs.readdir).toHaveBeenCalled();
await expect(promise).rejects.toEqual(new Error("mock error"));
});
it("returns an array of the .json file name strings in the test directory (and not any other files)", async () => {
const allPotentialFiles = ["non-json.txt", "test-json-1.json", "test-json-2.json"];
const onlyJsonFiles = ["test-json-1.json", "test-json-2.json"];
// Mock readdir to return all potential files from the dir
fs.readdir = jest.fn().mockResolvedValueOnce(allPotentialFiles);
// Get the promise
const promise = fileService.getJsonFiles({ folderPath: "mockPath", logActions: false });
expect(fs.readdir).toBeCalled();
await expect(promise).resolves.toEqual(onlyJsonFiles); // function should only return the json files
});
});
});

文件.service.ts

import fs from "fs/promises";
export default class FileService {
constructor() {}
async getJsonFiles(args: FilesListArgs): Promise<string[]> {
const { folderPath, logActions } = args;
try {
// Get list of all files
const files = await fs.readdir(folderPath);
// Filter to only include JSON files
const jsonFiles = files.filter((file) => {
return file.includes(".json");
});
return jsonFiles;
} catch (e) {
throw e;
}
}
}

我知道这是一个旧线程,但在我的情况下,我想处理与readFile(在您的情况下是writeFile(不同的结果。因此,我使用了Estus Flask建议的解决方案,不同之处在于,我在每个测试中处理readFile的每个实现,而不是使用mockResolvedValue

我也在用打字稿。

import { getFile } from './configFiles';
import fs from 'fs';
jest.mock('fs', () => {
return {
promises: {
readFile: jest.fn()
}
};
});
describe('getFile', () => {
beforeEach(() => {
jest.resetAllMocks();
});
it('should return results from file', async () => {
const mockReadFile = (fs.promises.readFile as jest.Mock).mockImplementation(async () =>
Promise.resolve(JSON.stringify('some-json-value'))
);
const res = await getFile('some-path');
expect(mockReadFile).toHaveBeenCalledWith('some-path', { encoding: 'utf-8' });
expect(res).toMatchObject('some-json-value');
});
it('should gracefully handle error', async () => {
const mockReadFile = (fs.promises.readFile as jest.Mock).mockImplementation(async () =>
Promise.reject(new Error('not found'))
);
const res = await getFile('some-path');
expect(mockReadFile).toHaveBeenCalledWith('some-path', { encoding: 'utf-8' });
expect(res).toMatchObject('whatever-your-fallback-is');
});
});

注意,为了使其适用于TS,我不得不将fs.promises.readFile转换为jest.Mock

另外,我的configFiles.ts看起来是这样的:

import { promises as fsPromises } from 'fs';
const readConfigFile = async (filePath: string) => {
const res = await fsPromises.readFile(filePath, { encoding: 'utf-8' });
return JSON.parse(res);
};
export const getFile = async (path: string): Promise<MyType[]> => {
try {
const fileName = 'some_config.json';
return readConfigFile(`${path}/${fileName}`);
} catch (e) {
// some fallback value
return [{}];
}
};

最新更新