断言 forEach 循环内的 catch 块代码
Asserting on catch block code inside forEach loop
我很难编写测试来断言在 forEach
循环内执行的 catch 块内发生的事情。
产品代码
function doSomething(givenResourceMap) {
givenResourceMap.forEach(async (resourceUrl) => {
try {
await axios.delete(resourceUrl);
} catch (error) {
logger.error(`Error on deleting resource ${resourceUrl}`);
logger.error(error);
throw error;
}
});
我想断言 logger.error
被调用了两次并且每次都使用正确的参数调用。所以我写了一些这样的测试
describe('Do Something', () => {
it('should log message if fail to delete the resource', function() {
const resource1Url = chance.url();
const givenResourceMap = new Map();
const thrownError = new Error('Failed to delete');
givenResourceMap.set(resource1Url);
sinon.stub(logger, 'error');
sinon.stub(axios, 'delete').withArgs(resource1Url).rejects(thrownError);
await doSomething(givenResourceMap);
expect(logger.error).to.have.callCount(2);
expect(logger.error.getCall(0).args[0]).to.equal(`Error deleting resource ${resource1Url}`);
expect(logger.error.getCall(1).args[0]).to.equal(thrownError);
// Also need to know how to assert about `throw error;` line
});
});
我正在使用 Mocha、sinon-chai,期待测试。上面的测试失败说 logger.error
是 0 次。
谢谢。
问题是您在 return 没有 Promise
的函数上使用 await
。请注意,doSomething
不是 async
,也不是 return 一个 Promise
对象。
forEach
函数是 async
但这意味着他们会 return 立即解决 Promise
而你永远不会 await
在他们身上。
实际上,doSomething
会在 forEach
内部的工作完成之前 return,这可能不是您想要的。为此,您可以使用像这样的常规 for 循环:
async function doSomething(givenResourceMap) {
for (const resourceUrl of givenResourceMap) {
try {
await axios.delete(resourceUrl);
} catch (error) {
logger.error(`Error on deleting resource ${resourceUrl}`);
logger.error(error);
throw error;
}
}
}
请注意,它将 doSomething
的 return 类型更改为 Promise
对象,而不是像最初那样 returning undefined
。但它确实允许您在测试中按照您的意愿对其执行 await
(并且可能也在生产代码中)。
但是由于您重新抛出循环中捕获的异常,您的测试将异常退出。测试代码也必须更改以捕获预期的错误:
it('should log message if fail to delete the resource', function(done) {
// ... the setup stuff you had before...
await doSomething(givenResourceMap).catch(err => {
expect(logger.error).to.have.callCount(2);
expect(logger.error.getCall(0).args[0]).to.equal(`Error deleting resource ${resource1Url}`);
expect(logger.error.getCall(1).args[0]).to.equal(thrownError);
done();
});
});
我很难编写测试来断言在 forEach
循环内执行的 catch 块内发生的事情。
产品代码
function doSomething(givenResourceMap) {
givenResourceMap.forEach(async (resourceUrl) => {
try {
await axios.delete(resourceUrl);
} catch (error) {
logger.error(`Error on deleting resource ${resourceUrl}`);
logger.error(error);
throw error;
}
});
我想断言 logger.error
被调用了两次并且每次都使用正确的参数调用。所以我写了一些这样的测试
describe('Do Something', () => {
it('should log message if fail to delete the resource', function() {
const resource1Url = chance.url();
const givenResourceMap = new Map();
const thrownError = new Error('Failed to delete');
givenResourceMap.set(resource1Url);
sinon.stub(logger, 'error');
sinon.stub(axios, 'delete').withArgs(resource1Url).rejects(thrownError);
await doSomething(givenResourceMap);
expect(logger.error).to.have.callCount(2);
expect(logger.error.getCall(0).args[0]).to.equal(`Error deleting resource ${resource1Url}`);
expect(logger.error.getCall(1).args[0]).to.equal(thrownError);
// Also need to know how to assert about `throw error;` line
});
});
我正在使用 Mocha、sinon-chai,期待测试。上面的测试失败说 logger.error
是 0 次。
谢谢。
问题是您在 return 没有 Promise
的函数上使用 await
。请注意,doSomething
不是 async
,也不是 return 一个 Promise
对象。
forEach
函数是 async
但这意味着他们会 return 立即解决 Promise
而你永远不会 await
在他们身上。
实际上,doSomething
会在 forEach
内部的工作完成之前 return,这可能不是您想要的。为此,您可以使用像这样的常规 for 循环:
async function doSomething(givenResourceMap) {
for (const resourceUrl of givenResourceMap) {
try {
await axios.delete(resourceUrl);
} catch (error) {
logger.error(`Error on deleting resource ${resourceUrl}`);
logger.error(error);
throw error;
}
}
}
请注意,它将 doSomething
的 return 类型更改为 Promise
对象,而不是像最初那样 returning undefined
。但它确实允许您在测试中按照您的意愿对其执行 await
(并且可能也在生产代码中)。
但是由于您重新抛出循环中捕获的异常,您的测试将异常退出。测试代码也必须更改以捕获预期的错误:
it('should log message if fail to delete the resource', function(done) {
// ... the setup stuff you had before...
await doSomething(givenResourceMap).catch(err => {
expect(logger.error).to.have.callCount(2);
expect(logger.error.getCall(0).args[0]).to.equal(`Error deleting resource ${resource1Url}`);
expect(logger.error.getCall(1).args[0]).to.equal(thrownError);
done();
});
});