我正在使用自定义redux中间件构建react-redux应用程序。 在我的项目定义中,action仅提供了一个对象来定义中间件和reducer的操作类型和必要参数。所有的ajax请求都将由中间件处理。这是生命周期看起来像: 行动 - >中间件(如果拦截了动作) - >减速机 - >存储
当用户尝试登录时,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
}
}
然后中间件将使用在操作中传递的参数来发送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中所做的是改变系统状态。如果状态为true,则前端将呈现信息页面。如果状态为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);
});
我发送登录操作。然后监视是否在100ms内正确调用authSucceed action和authFail操作。如果只运行一个测试,则此方法有效。如果有多个测试按顺序运行,它们可能会相互影响。我必须调整setTimeout的时间延迟,使其适用于所有情况,即10毫秒。 这样我感觉不舒服。我无法确定它是否适合我或每个人,因为绝对时间与硬件有关。 如果有人能就如何测试这个自定义中间件给我一些建议,我真的很感激。
答案 0 :(得分:0)
您的代码运行正常,但由于使用nock立即生成远程resquest响应,因此您不需要setTimeout
这么长时间。问题是承诺排队微任务,它们只在完成 macrotask 之后(在您的情况下,it()
),在同一个事件循环中运行。
这就是为什么你需要setTimeout
排队另一个macrotask,时间没有区别。我相信setImmediate
也应该有用。