如何使用 thunk 操作对 mapDispatchToProps 进行单元测试
How to unit test mapDispatchToProps with thunk action
我有以下 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"
时,dispatch
会通过 keyDown
操作调用。这是我的测试:
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(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
return相同的函数。
正如@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
最简单的解决方案可能是按照另一个答案 (+1) 中的建议记忆 keyDown()
。这是一种尝试涵盖所有基础的不同方法...
由于 keyDown()
是从 actions
导入的,我们可以 stub
return 的函数
每当使用 keyCode
:
调用时,一个 dummy
值
import * as actions;
keyDown = stub(actions, "keyDown");
keyDown.withArgs(keyCode).returns(dummy);
然后,我们的 unit tests 将验证 dispatch
是使用我们之前设置的 dummy
调用的。我们知道 dummy
只能被我们存根的 keyDown()
编辑 return,所以这个检查也验证了 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;
我们还应该 test keyDown()
本身通过验证给定的 dispatch
回调是用正确的键事件和键调用来隔离的
代码:
expect(dispatch).to.have.been.calledWith({type: actions.KEYDOWN, key: keyCode});
链接
我有以下 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"
时,dispatch
会通过 keyDown
操作调用。这是我的测试:
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(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
return相同的函数。
正如@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
最简单的解决方案可能是按照另一个答案 (+1) 中的建议记忆 keyDown()
。这是一种尝试涵盖所有基础的不同方法...
由于 keyDown()
是从 actions
导入的,我们可以 stub
return 的函数
每当使用 keyCode
:
dummy
值
import * as actions;
keyDown = stub(actions, "keyDown");
keyDown.withArgs(keyCode).returns(dummy);
然后,我们的 unit tests 将验证 dispatch
是使用我们之前设置的 dummy
调用的。我们知道 dummy
只能被我们存根的 keyDown()
编辑 return,所以这个检查也验证了 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;
我们还应该 test keyDown()
本身通过验证给定的 dispatch
回调是用正确的键事件和键调用来隔离的
代码:
expect(dispatch).to.have.been.calledWith({type: actions.KEYDOWN, key: keyCode});
链接