NodeJS 在函数fs.writefile中模拟回调

9wbgstp7  于 2022-11-22  发布在  Node.js
关注(0)|答案(3)|浏览(181)

我在这个问题上纠缠了3个小时。我没有找到一个解决方案,如何测试以下代码中的if(err)分支:

function createFile(data){

return new Promise(function(resolve, reject) {

    try {

        if(data === null || data === undefined){
            throw new Error(errorMessages.noDataDefined);
        }

        let internalJobId = uuid.v4();
        let fileName = 'project_name' + internalJobId + '.xml';

        fs.writeFile(config.tmpPath + fileName, data, function (err) {
            if (err){
                throw new Error(err.toString());
            } else {
                resolve(fileName);
            }
            
        });

    } catch (error) {
        return reject(error);
    }
});

}
此测试通过,但不调用if(err){ throw new Error(err.toString())}
我必须找到一个解决方案,回调如何返回错误,但我没有得到正确的解决方案。

test('Error', () => {

    jest.mock('fs', () => ({
        writeFile: jest.fn((path, data, callback) => callback(Error('some error')))
      }));

    return expect(createFile('Does not matter')).rejects.toThrow('some error');

});

但是有了这个测试,甚至没有一个拒绝,所以从来没有抛出一个错误。我将不胜感激,如果有人能帮助我在那里。

j2cgzkjk

j2cgzkjk1#

这里 有 两 个 问题 , 一 个 是 fs.writeFile 没有 被 正确 地 模拟 , 另 一 个 是 createFile 没有 正确 地 处理 错误 , 不能 达到 预期 。
jest.mock 会 影响 那些 还 没有 被 导入 并 被 提升 到 块 顶部 的 模块 ( 或者 在 顶层 使用 时 , 提升 到 导入 模块 之上 ) 。 如果 使用 fs 的 模块 已经 被 导入 , 则 它 不会 影响 fs 。 由于 fs 函数 通常 与 它们 的 命名 空间 一起 使用 , 因此 它们 也 可以 被 模拟 为 方法 。
它 应该 是 :

// at top level
import fs from 'fs';
jest.mock('fs', ...);
...

中 的 每 一 个
或者 :

// inside test
jest.spyOn(fs, 'writeFile').mockImplementation(...);
...

格式
并 被 Assert 以 使 测试 更 具体 :

expect(fs.writeFile).toBeCalledTimes(1);
expect(fs.writeFile).toBeCalledWith(...);
return expect(createFile('Does not matter'))...

格式
Promise 构造 函数 不 需要 try..catch , 因为 它 已经 捕获 了 内部 的 所有 * synchronous * 错误 , 无法 从 回调 中 捕获 异步 错误 。 对于 需要 拒绝 Promise 的 地方 , 为了 一致 性 , reject 可以 被 首选 。
fs.writeFile 回调 内 抛出 错误 是 一 个 错误 , 并 导致 待定 承诺 。 它 没有 机会 拒绝 承诺 , 也 没有 机会 在 回调 外 被 try..catch 捕获 , 并 导致 未 捕获 的 错误 。
它 应该 是 :

function createFile(data){
    return new Promise(function(resolve, reject) {
        if(data === null || data === undefined){
            reject(new Error(errorMessages.noDataDefined));
        }

        let internalJobId = uuid.v4();
        let fileName = 'project_name' + internalJobId + '.xml';

        fs.writeFile(config.tmpPath + fileName, data, function (err) {
            if (err){
                reject(new Error(err.toString()); // reject(err) ?
            } else {
                resolve(fileName);
            }            
        });
    });
}

格式
为了 使 嵌套 最 小 化 , 可以 将 不 需要 承诺 的 部分 移 到 构造 函数 之外 , 函数 为 async

async function createFile(data){
    if(data === null || data === undefined){
        throw new Error(errorMessages.noDataDefined);
    }

    return new Promise(function(resolve, reject) {    
        let internalJobId = uuid.v4();
        ...

格式
还有 可能 不 需要 承诺 的 fs.promises API 。
另 请 注意 , new Error(err.toString()) 可能 是 不 必要 的 , 并 会 导致 未 预期 的 错误 消息 , 因此 宣告 失败 。 可以 使用 err 来 拒绝 此 承诺 。 如果 目的 是 移除 不 必要 的 错误 信息 或 变更 错误 堆栈 , 则 应该 使用 new Error(err.message)

col17t5w

col17t5w2#

解决方案是:

jest.spyOn(fs, 'writeFile').mockImplementation((f, d, callback) => {
                callback('some error');
 });

感谢埃斯图斯·弗拉斯克!

ykejflvf

ykejflvf3#

下面的三行代码为我完成了这项工作:

import * as fs from 'fs/promises';

jest.mock('fs/promises');

jest.spyOn(fs, 'writeFile').mockImplementation( your implementation here );

相关问题