Javascript 在Mocha、Chai和Sinon完成承诺后测试非承诺
我已经在Redux中创建了一个小的学习项目,并且从本质上复制了用于异步操作的中间件。现在我想为它写一些测试,以确保在我改变一些事情后它能正常工作 为了编写我的测试,我使用了摩卡,chai,chai和sinon 我对redux没有任何问题,但我不确定如何测试 中间件中的相关代码:Javascript 在Mocha、Chai和Sinon完成承诺后测试非承诺,javascript,node.js,unit-testing,mocha.js,redux,Javascript,Node.js,Unit Testing,Mocha.js,Redux,我已经在Redux中创建了一个小的学习项目,并且从本质上复制了用于异步操作的中间件。现在我想为它写一些测试,以确保在我改变一些事情后它能正常工作 为了编写我的测试,我使用了摩卡,chai,chai和sinon 我对redux没有任何问题,但我不确定如何测试 中间件中的相关代码: export default function callApiMiddleware({ dispatch }) { return next => action => { // ... variou
export default function callApiMiddleware({ dispatch }) {
return next => action => {
// ... various checks
const [ requestType, successType, errorType ] = types;
dispatch({ ...payload, type: requestType });
return callApi().then(
response => dispatch({ ...payload, type: successType, response }),
error => dispatch({ ...payload, type: errorType, error })
);
}
}
无论承诺是否履行,中间件都会分派适当的操作
为了测试这一点,我将除掉分派函数和其他函数,并且我还传递了一个虚拟承诺,根据我想要测试的内容来实现或拒绝
我编写的测试如下所示:
it('should trigger success action when successful', () => {
let promise = callApiMiddleware({ dispatch, getState })(next)({
...action,
callApi: () => new Promise((resolve, reject) => resolve('SUCCESS'))
});
return expect(promise).to.eventually.be.fulfilled;
});
export default function callApiMiddleware({ dispatch }) {
return next => action => {
// ... various checks
const [ requestType, successType, errorType ] = types;
dispatch({ ...payload, type: requestType });
return callApi().then(
response => dispatch({ ...payload, type: successType, response }),
error => {
dispatch({ ...payload, type: errorType, error });
throw error;
}
);
}
}
这很好,但我遇到的第一个问题是,当我试图模拟一个可能因没有互联网连接而被拒绝的承诺时,我写了这个测试:
it('should trigger failure action when failure occurs', () => {
let promise = callApiMiddleware({ dispatch, getState })(next)({
...action,
callApi: () => new Promise((resolve, reject) => reject('ERROR'))
});
return expect(promise).to.eventually.be.rejected;
});
但此操作失败,并显示以下消息:
AssertionError:预期承诺将被拒绝,但未定义的
预期:[未定义]
实际:[未定义]
这对我来说毫无意义,因为我明确地传递了一个承诺,唯一的功能就是被拒绝
我的另一个问题是,我还想做出其他断言,这些断言不一定与承诺本身有任何关系,但必须在承诺完成时进行评估,例如,我想断言dispatch
方法被调用了两次。dispatch
方法本身在使用sinon的测试中被删除,以执行所需的断言。我可能希望以这种方式做出多个断言
我尝试了以下方法:
it('should trigger success action when successful', () => {
let promise = callApiMiddleware({ dispatch, getState })(next)({
...action,
callApi: () => new Promise((resolve, reject) => resolve('SUCCESS'))
});
return Q.all([
expect(promise).to.eventually.be.fulfilled,
expect(dispatch).to.eventually.be.calledTwice
]);
});
但这返回了一个非常大的错误,它告诉我dispatch
不是表
,也就是说,不是承诺
我不知道该怎么做,所以任何输入都将不胜感激。承诺不会重发错误,因此,如果您在第一个捕获处理程序中捕获错误,除非您再次抛出捕获的错误,否则承诺将在下一个处理程序中实现
// this does not work
promise.catch((e) => console.log(e)).catch((e) => console.log(e));
如果你想让它工作,你必须重新显示一个错误
promise.catch((e) => {
console.log(e);
throw e;
}).catch((e) => console.log(e));
若希望测试通过,则需要重新显示在承诺的捕获处理程序中捕获的错误。因此,您的代码应该如下所示:
it('should trigger success action when successful', () => {
let promise = callApiMiddleware({ dispatch, getState })(next)({
...action,
callApi: () => new Promise((resolve, reject) => resolve('SUCCESS'))
});
return expect(promise).to.eventually.be.fulfilled;
});
export default function callApiMiddleware({ dispatch }) {
return next => action => {
// ... various checks
const [ requestType, successType, errorType ] = types;
dispatch({ ...payload, type: requestType });
return callApi().then(
response => dispatch({ ...payload, type: successType, response }),
error => {
dispatch({ ...payload, type: errorType, error });
throw error;
}
);
}
}
承诺不会重新抛出错误,所以如果您在第一个catch处理程序中捕获到错误,则承诺将在下一个处理程序中实现,除非您再次抛出捕获的错误
// this does not work
promise.catch((e) => console.log(e)).catch((e) => console.log(e));
如果你想让它工作,你必须重新显示一个错误
promise.catch((e) => {
console.log(e);
throw e;
}).catch((e) => console.log(e));
若希望测试通过,则需要重新显示在承诺的捕获处理程序中捕获的错误。因此,您的代码应该如下所示:
it('should trigger success action when successful', () => {
let promise = callApiMiddleware({ dispatch, getState })(next)({
...action,
callApi: () => new Promise((resolve, reject) => resolve('SUCCESS'))
});
return expect(promise).to.eventually.be.fulfilled;
});
export default function callApiMiddleware({ dispatch }) {
return next => action => {
// ... various checks
const [ requestType, successType, errorType ] = types;
dispatch({ ...payload, type: requestType });
return callApi().then(
response => dispatch({ ...payload, type: successType, response }),
error => {
dispatch({ ...payload, type: errorType, error });
throw error;
}
);
}
}
使用
undefined
实现承诺的原因是中间件返回的结果:
return callApi().then(
response => dispatch({ ...payload, type: successType, response }),
error => dispatch({ ...payload, type: errorType, error })
);
由于它不会在第二次回调中重新抛出错误,因此实现了最终的承诺。由于它也不返回任何内容,因此使用undefined
来实现
在这种情况下,您可以更改代码以重新显示错误:
return callApi().then(
response => dispatch({ ...payload, type: successType, response }),
error => {
dispatch({ ...payload, type: errorType, error })
throw error;
}
);
这将给出您期望的结果,但每次请求失败时,都会在DevTools中报告未处理的拒绝。这对你来说可能没问题
至于你的第二个例子:
但这返回了一个非常大的错误,它简单地告诉我调度是不可能的,即不是一个承诺
它看起来像是.to.finally.
。实际上,dispatch
并不是一个承诺,所以你不能这样使用它。您可能想写这样的内容:
return expect(promise).to.eventually.be.fulfilled.then(() => {
expect(dispatch).to.be.calledTwice();
});
最后,我鼓励你退房。使用生成器描述副作用可能比使用自定义中间件更容易,而且生成器确实如此。使用未定义的
实现承诺的原因是因为中间件会返回以下内容:
return callApi().then(
response => dispatch({ ...payload, type: successType, response }),
error => dispatch({ ...payload, type: errorType, error })
);
由于它不会在第二次回调中重新抛出错误,因此实现了最终的承诺。由于它也不返回任何内容,因此使用undefined
来实现
在这种情况下,您可以更改代码以重新显示错误:
return callApi().then(
response => dispatch({ ...payload, type: successType, response }),
error => {
dispatch({ ...payload, type: errorType, error })
throw error;
}
);
这将给出您期望的结果,但每次请求失败时,都会在DevTools中报告未处理的拒绝。这对你来说可能没问题
至于你的第二个例子:
但这返回了一个非常大的错误,它简单地告诉我调度是不可能的,即不是一个承诺
它看起来像是.to.finally.
。实际上,dispatch
并不是一个承诺,所以你不能这样使用它。您可能想写这样的内容:
return expect(promise).to.eventually.be.fulfilled.then(() => {
expect(dispatch).to.be.calledTwice();
});
最后,我鼓励你退房。使用生成器描述副作用可能比使用自定义中间件更容易,而且生成器确实如此。虽然这并不能回答您的问题,但您可能会发现测试起来要容易得多。谢谢!我没有听说过,我一定会去看看,但我遇到的问题可能不是特定于redux的,而是我正在尝试解决的测试工具。虽然这并不能回答您的问题,但您可能会发现测试更容易。谢谢!我没有听说过,我肯定会去看看,但我遇到的问题可能不是特定于redux,而是我正在尝试解决的测试工具。谢谢,我没有意识到这种行为。这确实解决了我的第一个问题。谢谢你,我没有意识到这种行为。这确实解决了我的第一个问题。谢谢你提供了一个非常完整的答案,我对测试工具不是很熟悉,所以这非常有帮助。我一定会去看看redux传奇,它看起来是一种更干净的方法,完全符合我的目标。谢谢你提供了一个非常完整的答案,我对测试工具不是很熟悉,所以这非常有帮助。我一定会看看redux传奇,它看起来是一种更干净的方式,完全符合我的目标。