Jest - 函数中的模拟回调 fs.writefile
Jest - Mock callback in function fs.writefile
我在这个话题上坚持了 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())}
我必须找到一个解决方案,怎么回调returns一个错误,但我没有得到正确的解决方案。
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');
});
但是这个测试甚至没有拒绝,所以从来没有抛出错误。如果有人能帮助我,我将不胜感激。
这里有两个问题。一是 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
因为它已经在其中捕获了所有 同步 错误并且无法从回调中捕获异步错误。对于需要拒绝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);
}
});
});
}
为了尽量减少嵌套,不需要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)
.
解决方案是:
jest.spyOn(fs, 'writeFile').mockImplementation((f, d, callback) => {
callback('some error');
});
感谢 Estus Flask!
我在这个话题上坚持了 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())}
我必须找到一个解决方案,怎么回调returns一个错误,但我没有得到正确的解决方案。
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');
});
但是这个测试甚至没有拒绝,所以从来没有抛出错误。如果有人能帮助我,我将不胜感激。
这里有两个问题。一是 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
因为它已经在其中捕获了所有 同步 错误并且无法从回调中捕获异步错误。对于需要拒绝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);
}
});
});
}
为了尽量减少嵌套,不需要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)
.
解决方案是:
jest.spyOn(fs, 'writeFile').mockImplementation((f, d, callback) => {
callback('some error');
});
感谢 Estus Flask!