如何使用 thunk 操作对 mapDispatchToProps 进行单元测试
Posted
技术标签:
【中文标题】如何使用 thunk 操作对 mapDispatchToProps 进行单元测试【英文标题】:How to unit test mapDispatchToProps with thunk action 【发布时间】:2017-05-28 17:57:03 【问题描述】:我有以下 Redux 动作创建者:
export const keyDown = key => (dispatch, getState) =>
const modifier = getState().data;
dispatch( type: KEYDOWN, key );
return handle(modifier, key); // Returns true or false
;
还有以下连通分量:
export const mapDispatchToProps = dispatch => (
onKeyDown: e =>
if(e.target.tagName === "INPUT") return;
const handledKey = dispatch(keyDown(e.keyCode));
if(handledKey)
e.preventDefault();
);
我正在尝试编写一个测试,以确保当tagName
不是"INPUT"
时,使用keyDown
操作调用dispatch
。这是我的测试:
import spy from "sinon";
import keycode from "keycodes";
import mapDispatchToProps from "./connected-component";
import keyDown from "./actions";
// Creates a simple Event stub...
const createEvent = (tag, keyCode) => (
target:
tagName: tag.toUpperCase()
,
preventDefault: spy(),
keyCode
);
it("Dispatches a keyDown event with the specified keyCode if the selected element is not an <input>", () =>
const dispatch = spy();
const keyCode = keycode("u");
mapDispatchToProps(dispatch).onKeyDown(createEvent("div", keyCode));
// This fails...
expect(dispatch).to.have.been.calledWith(keyDown(keycode));
);
大概这与使用箭头函数有关?有什么方法可以确保使用我期望的函数签名调用调度?
【问题讨论】:
那么您是在测试字符串相等性和返回语句是否有效,还是开发人员没有意外删除它?上帝我不喜欢大多数单元测试:( 主要是实际调用了dispatch
。很多时候,我调用动作创建者而不传递调度。检查 keyDown 动作是否通过也很重要,所以我认为 expect(dispatch).to.have.been.called
还不够
【参考方案1】:
keyDown(keycode)
每次都会创建一个新函数,而且每个函数实例都不一样,测试用例按预期失败。
这可以通过记忆keyDown
创建的函数来解决:
let cacheKeyDown = ;
export const keyDown = key => cacheKeyDown[key] || cacheKeyDown[key] = (dispatch, getState) =>
const modifier = getState().data;
dispatch( type: KEYDOWN, key );
return handle(modifier, key);
;
通过记忆,keyDown
调用相同的keycode
返回相同的函数。
【讨论】:
【参考方案2】:正如@DarkKnight 所说(获得+1),keyDown
为每次调用返回一个新函数,因此测试失败,因为keyDown(keyCode)
!= keyDown(keyCode)
。
如果您不想更改keyDown
的实际实现,您可以在测试中模拟:
import * as actions from "./actions";
spyOn(actions, 'keyDown');
您可以查看有关如何完成此操作的其他答案:
How to mock the imports of an ES6 module?
How to mock dependencies for unit tests with ES6 Modules
【讨论】:
【参考方案3】:最简单的解决方案可能是记忆 keyDown()
,正如另一个答案 (+1) 中所建议的那样。这是一种尝试涵盖所有基础的不同方法...
由于keyDown()
是从actions
导入的,我们可以stub
返回的函数
一个 dummy
值,每当它被 keyCode
调用时:
import * as actions;
keyDown = stub(actions, "keyDown");
keyDown.withArgs(keyCode).returns(dummy);
然后,我们的 unit tests 将验证 dispatch
是使用我们之前设置的 dummy
调用的。我们知道 dummy
只能由我们的存根 keyDown()
返回,所以这个检查也验证了 keyDown()
被调用了。
mapDispatchToProps(dispatch).onKeyDown(createEvent("div", keyCode));
expect(dispatch).to.have.been.calledWith(dummy);
expect(keyDown).to.have.been.calledWithExactly(keyCode);
为了彻底,我们应该添加单元测试以确认当目标是<input>
时,关键事件没有分派。
mapDispatchToProps(dispatch).onKeyDown(createEvent("input", keyCode));
expect(dispatch).to.not.have.been.called;
expect(keyDown).to.not.have.been.called;
我们还应该通过验证给定的 dispatch
回调是使用正确的键事件和键调用来隔离test keyDown()
本身
代码:
expect(dispatch).to.have.been.calledWith(type: actions.KEYDOWN, key: keyCode);
链接
GitHub demo 100% test coverage【讨论】:
以上是关于如何使用 thunk 操作对 mapDispatchToProps 进行单元测试的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 Redux connect 中的操作对测试组件进行快照?
使用C++的函数memset()时要注意它的操作对象是每一个字节