【发布时间】:2017-02-22 20:22:12
【问题描述】:
我正在构建一个 react-redux 应用程序,使用自定义 redux 中间件。 在我的项目的定义中,action只提供了一个对象来定义中间件和reducer的action类型和必要的参数。所有的 ajax 请求都将由中间件处理。这是生命周期的样子: action -> 中间件(如果 action 被拦截) -> reducer -> store
当用户尝试登录时,react 组件上的操作会触发一个动作,如下所示:
export function login(username, password) {
return {
type: 'LOGIN',
username: username,
password: password
}
}
export function authSucceed(username, isAdmin) {
return {
type: 'AUTHSUCCEED',
username: username,
isAdmin: isAdmin
}
}
export function authFail(text) {
return {
type: 'AUTHFAIL',
errorMessage: text
}
}
然后中间件会使用action中传入的参数来发送ajax请求,大概是这样的。
export function customedMiddleware(store) {
return next => action => {
if (action.type === 'LOGIN') {
axios.post(url + '/api/login', {
username: action.username,
password: action.password
})
.then(res => {
if (res.status === 200) {
store.dispatch(actions.authSucceed(res.data.username, res.data.isAdmin));
} else {
store.dispatch(actions.authFail(res.data));
}
})
.catch(error => console.log(error));
}
return next(action);
};
}
中间件向服务器发送登录请求后,根据认证是否成功,中间件会相应地在reducer中派发一些动作。由于 authSucceed 和 authFail 不会被中间件拦截,reducer 会做相应的处理。
export default function(state = false, action) {
switch(action.type) {
case 'AUTHSUCCEED':
return true;
case 'AUTHFAIL':
return false;
case 'LOGOUT':
return false;
}
return state;
}
这里在 reducer 中所做的是改变系统状态。如果状态为真,前端会渲染信息页面。如果状态为false,则前端将停留在登录页面。
我喜欢这样的系统定义。每个 MVC 部分都很好地隔离。但是,测试中间件非常困难。目前,我正在以这种方式进行测试:
it('should dispatch authSucceed if signup with correct info', () => {
nock('http://localhost:8080')
.post('/api/signup', {
username: 'bruce',
password: 'Gx1234'
})
.reply(200, {
username: 'bruce',
isAdmin: false
});
const createStoreWithMiddleware = applyMiddleware(customedMiddleware)(createStore);
const store = createStoreWithMiddleware(reducers);
const dispatch = sinon.spy(store, 'dispatch');
store.dispatch(actions.login('bruce', 'Gx1234'));
setTimeout(() => {
expect(dispatch.calledWith({
type: 'AUTHSUCCEED',
username: 'bruce',
isAdmin: false
})).to.be.true;
}, 100);
});
我发送登录操作。然后窥探 authSucceed 动作和 authFail 动作是否会在 100 毫秒内正确调用。如果只有一个测试要运行,则此方法有效。如果按顺序运行不止一个测试,它们可能会相互影响。我必须调整 setTimeout 的时间延迟以使其适用于所有情况,即 10 毫秒。 这种方式我感觉不舒服。我无法确定它是否适合我或所有人,因为绝对时间与硬件有关。 如果有人能给我一些关于如何测试这个自定义中间件的建议,我将不胜感激。
【问题讨论】:
-
如果你使用
setTimeout(..., 0)会发生什么? -
redux.js.org/docs/recipes/… 这是一个很好的资源。另外,为什么不使用 redux-thunk?
-
实际上,setTimeout(..., 0) 有效。
-
我尝试使用 redux-thunk。但是,我找到的参考资料使用 redux 来测试异步操作。那不是我的情况。我使用中间件来处理 ajax 请求和响应。但我会更深入地研究 thunk 以更好地理解它是如何工作的。感谢您的建议。
标签: ajax testing redux middleware