无法弄清楚如何使用 redux-saga-test-plan 测试 redux-saga 功能
Posted
技术标签:
【中文标题】无法弄清楚如何使用 redux-saga-test-plan 测试 redux-saga 功能【英文标题】:Cannot figure out how to test redux-saga function with redux-saga-test-plan 【发布时间】:2017-04-07 19:50:21 【问题描述】:我正在自学如何使用redux-saga
,同时自学单元测试,特别是 Jest。我从 redux-saga 的文档中获取了一个示例 saga,在这里:
http://yelouafi.github.io/redux-saga/docs/advanced/NonBlockingCalls.html
...并出于我自己的目的对其进行了修改。它应该是一个简单的身份验证处理程序,它侦听登录或注销操作(因为该函数不知道用户是否已登录),然后采取适当的行动。我已经在应用程序中测试了该功能,它似乎按预期运行,这很棒。函数如下:
function* authFlow()
while (true)
const initialAction = yield take (['LOGIN', 'LOGOUT']);
if (initialAction.type === 'LOGIN')
const username, password = initialAction.payload;
const authTask = yield fork(
authorizeWithRemoteServer,
username: username, password: password
);
const action = yield take(['LOGOUT', 'LOGIN_FAIL']);
if (action.type === 'LOGOUT')
yield cancel(authTask);
yield call (unauthorizeWithRemoteServer)
else
yield call (unauthorizeWithRemoteServer)
这看起来相当简单,但我很难测试它。以下是我的基于 Jest 的测试脚本的注释版本:
it ('authFlow() should work with successful login and then successful logout', () =>
const mockCredentials =
username: 'User',
password: 'goodpassword'
;
testSaga( stateAuth.watcher )
// This should test the first 'yield', which is
// waiting for LOGIN or LOGOUT. It works
.next()
.take(['LOGIN', 'LOGOUT'])
// This should test 'authorizeWithRemoteServer',
// and appears to do that properly
.next(
type: 'LOGIN',
payload: mockCredentials
)
.fork(
stateAuth.authorizeWithRemoteServer,
mockCredentials)
// This should reflect 'yield take' after the 'yield fork',
// and does so
.next()
.take(['LOGOUT', 'LOGIN_FAIL'])
/*
This is where I don't understand what's happening.
What I would think I should do is something like this,
if I want to test the logout path:
.next( type: 'LOGOUT' )
.cancel(createMockTask())
...but that results in the following, perhaps predictable, error:
cancel(task): argument task is undefined
What I found does make the test not fail is the following line, but
I do not understand why it works. The fact that it matches
"take(['LOGIN', 'LOGOUT'])" indicates that it has
looped back to the top of the generator
*/
.next(createMockTask())
.take(['LOGIN', 'LOGOUT'])
)
所以要么我做错了 sagas,要么我不明白如何测试 sagas,或者测试这种 sagas 真的很难而且可能不切实际。
那么这里发生了什么?提前致谢!
【问题讨论】:
【参考方案1】:不知道答案是否仍然与您相关,但以防万一其他人将来偶然发现此问题:
排队
.next().take(['LOGOUT', 'LOGIN_FAIL'])
你基本上是通过undefined
,这意味着这一行的产量:
const action = yield take(['LOGOUT', 'LOGIN_FAIL']);
使action
成为undefined
。
你应该做的是在该行传递模拟任务:
.next(createMockTask()).take(['LOGOUT', 'LOGIN_FAIL'])
我认为这将是正确的测试
it ('authFlow() should work with successful login and then successful logout', () =>
const mockCredentials = username: 'User', password: 'goodpassword';
testSaga( stateAuth.watcher )
//this should test the first 'yield', which is waiting for LOGIN or LOGOUT. It works
.next().take(['LOGIN', 'LOGOUT'])
// This should test 'authorizeWithRemoteServer', and appears to do that properly
.next(type: 'LOGIN', payload: mockCredentials).fork( stateAuth.authorizeWithRemoteServer, mockCredentials)
// We pass a mock task here
.next(createMockTask()).take(['LOGOUT', 'LOGIN_FAIL'])
// And then this should be correct
.next(type: 'LOGOUT').cancel(createMockTask())
// after which the saga loops back
.take(['LOGIN', 'LOGOUT'])
)
请记住,当调用next()
时,您正在履行之前的收益。
更新:哎呀,createMockTask()
的结果应该被存储以便能够将其用于断言。这应该是正确的代码:
it ('authFlow() should work with successful login and then successful logout', () =>
const mockCredentials = username: 'User', password: 'goodpassword';
const mockTask = createMockTask();
testSaga( stateAuth.watcher )
//this should test the first 'yield', which is waiting for LOGIN or LOGOUT. It works
.next().take(['LOGIN', 'LOGOUT'])
// This should test 'authorizeWithRemoteServer', and appears to do that properly
.next(type: 'LOGIN', payload: mockCredentials).fork( stateAuth.authorizeWithRemoteServer, mockCredentials)
// We pass a mock task here
.next(mockTask).take(['LOGOUT', 'LOGIN_FAIL'])
// And then this should be correct
.next(type: 'LOGOUT').cancel(mockTask)
// after which the saga loops back
.take(['LOGIN', 'LOGOUT'])
)
【讨论】:
我尝试了你的建议(谢谢!),它让我更接近,但现在在线.next(type: actions.LOGOUT).cancel(createMockTask())
,我得到这个:断言4失败:取消效果不匹配,但是“预期" 和 "实际" 值匹配。我将在下一条评论中发布实际与预期的内容。
预期: '@@redux-saga/TASK': true, isRunning: [Function: isRunning],result: [Function: result], error: [Function: error], setRunning: [ Function: setRunning], setResult: [Function: setResult], setError: [Function: setError] 实际: '@@redux-saga/TASK': true, isRunning: [Function: isRunning],result: [Function: result ],错误:[函数:错误],setRunning:[函数:setRunning],setResult:[函数:setResult],setError:[函数:setError]
啊是的,我犯了一个错误,请参阅更新代码的答案。
啊!我想我没有意识到的一件事是,当您调用 createMockTask()
时,该实例具有唯一签名,因此我需要执行 const mockTask1 = createMockTask()
之类的操作,然后多次引用它,就像您在示例中所做的那样.我慢慢开始明白这一点。再次感谢。
是的,这正是问题所在。无论如何,很乐意提供帮助!以上是关于无法弄清楚如何使用 redux-saga-test-plan 测试 redux-saga 功能的主要内容,如果未能解决你的问题,请参考以下文章