【问题标题】:Jest - Mock callback in function fs.writefileJest - 函数 fs.writefile 中的模拟回调
【发布时间】:2021-01-22 19:35:41
【问题描述】:

我在这个话题上停留了 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');


});

但是通过这个测试甚至没有拒绝,所以永远不会抛出错误。如果有人可以帮助我,我将不胜感激。

【问题讨论】:

  • 请提供可以重现问题的stackoverflow.com/help/mcve。您省略了相关的代码。没有创建文件。如果它用new Promise 包装 fs.writeFile ,那么就会出现错误,因为在 fs.writeFile 回调中抛出错误不会导致 promise 被拒绝。
  • 您好,我已经更新了这个问题。我正在检查一个错误,抛出这个错误并在 catch 块中我拒绝这个错误。

标签: node.js callback jestjs mocking


【解决方案1】:

这里有两个问题。一是fs.writeFile 没有被正确地模拟。还有一个是createFile没有正确处理错误,不能达到预期。

jest.mock 影响尚未导入并提升到块顶部(或在顶层使用时高于导入)的模块。如果使用它的模块已经被导入,它不会影响fs。由于fs 函数通常与它们的命名空间一起使用,它们也可以被模拟为方法。

应该是:

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

或者:

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

并被断言使测试更具体:

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

Promise 构造函数不需要try..catch,因为它已经捕获了其中的所有同步错误,并且无法从回调中捕获异步错误。对于需要拒绝承诺的地方,可以首选reject 以保持一致性。

在fs.writeFile 回调中抛出错误是一个错误,并导致待处理的承诺。它没有机会拒绝promise,没有机会在回调之外被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);
            }            
        });
    });
}

为了尽量减少嵌套,可以将不需要promisification的部分移到构造函数之外,函数为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)。

【讨论】:

  • 感谢您的帮助。我以删除 try / catch 块的方式更改了代码。同样,第一个 if 语句在 promise 之上并且现在在一个 async func 中。但是所有更改都使代码更干净,但对于我的第一个问题,它似乎仍然没有任何改变。我仍然无法模拟这一行“reject(new Error(err.toString()); // reject(err) ?” 被覆盖的函数。我不知道,如果它只适合我,但我是新手在 Jest 中,发现 mocking 部分比开发中的大多数部分更复杂。
  • 这是您可能尚未遇到的问题的一部分。我更新了。这并不难,但可能需要了解它是如何与 Node 模块混淆的以及引擎盖下发生了什么才能舒适地使用它。
  • 不幸的是这个“jest.spyOn('fs', 'writeFile').mockImplementation(...);”导致“无法监视原始值;给定字符串”
  • 这是一个错字,已修复。
【解决方案2】:

解决办法是:

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

感谢 Estus Flask!

【讨论】:

  • 不客气。请注意,这是不符合标准的模拟,无法发现所描述的错误处理问题,因为它同步触发错误,而 writeFile 是异步的。应该是=> setTimeout(() => { callback('some error') })。
  • 好的,谢谢。我已经更改了代码。在这两种情况下,覆盖率都是 100%,但我理解这更符合要求。所以我现在想用 s3 Api 尝试一下,我认为这很容易。不得不开玩笑地学习很多东西,但总有一天我会正确理解一切,我希望;)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-09-14
  • 2018-10-01
  • 1970-01-01
  • 2020-11-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多