最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Testing with runSaga with take and delay - Stack Overflow

programmeradmin2浏览0评论

If I have a saga with this form:

function * sagaWorker() {
  yield put(START_ACTION)
  yield take(WAIT_FOR_ACTION)
  yield delay(100)
  yield put(END_ACTION)
}

I can successfully test it using runSaga like this:

step('saga passes the tests', async () => {
  const channel = stdChannel()
  const dispatched = []
  const options = {
    dispatch: action => dispatched.push(action),
    getState: () => {},
    channel
  }
  const task = runSaga(options, sagaWorker)
  channel.put(WAIT_FOR_ACTION)
  await task.toPromise()
  expect(dispatched).to.deep.eql([START_ACTION, END_ACTION])
})

However, if I move the delay in front of the take:

function * sagaWorker() {
  yield put(START_ACTION)
  yield delay(100)
  yield take(WAIT_FOR_ACTION)
  yield put(END_ACTION)
}

Now the saga doesn't run to pletion and times out - it gets to the take but the action never arrives in the channel.

Is it possible to test it using this form? I suspect I can make it work by calling the delays rather than yielding them directly but I'd like to know how to make it work without doing that (if it's possible).

If I have a saga with this form:

function * sagaWorker() {
  yield put(START_ACTION)
  yield take(WAIT_FOR_ACTION)
  yield delay(100)
  yield put(END_ACTION)
}

I can successfully test it using runSaga like this:

step('saga passes the tests', async () => {
  const channel = stdChannel()
  const dispatched = []
  const options = {
    dispatch: action => dispatched.push(action),
    getState: () => {},
    channel
  }
  const task = runSaga(options, sagaWorker)
  channel.put(WAIT_FOR_ACTION)
  await task.toPromise()
  expect(dispatched).to.deep.eql([START_ACTION, END_ACTION])
})

However, if I move the delay in front of the take:

function * sagaWorker() {
  yield put(START_ACTION)
  yield delay(100)
  yield take(WAIT_FOR_ACTION)
  yield put(END_ACTION)
}

Now the saga doesn't run to pletion and times out - it gets to the take but the action never arrives in the channel.

Is it possible to test it using this form? I suspect I can make it work by calling the delays rather than yielding them directly but I'd like to know how to make it work without doing that (if it's possible).

Share Improve this question asked May 15, 2019 at 12:28 Will JenkinsWill Jenkins 9,9171 gold badge29 silver badges48 bronze badges
Add a ment  | 

1 Answer 1

Reset to default 10

Using yield call(() => myPromiseyDelay(500)) won't save you here. There will still be nothing to notice the "lost" action at the time it's dispatched.

When you post your WAIT_FOR_ACTION the saga is is in a yielded state on the yield delay. There is no queue for actions here, so by the time you get to yield take(WAIT_FOR_ACTION), the WAIT_FOR_ACTION action has long since been dispatched, unnoticed by any of the saga logic you presented above (there was no active take to grab the action).

Consider setting up an actionChannel to capture these unlistened-for actions. They'll be queued up in the channel, ready-to-consume, after the delay has pleted.

So, something like:

function * sagaWorker() {
  const channel = yield actionChannel(WAIT_FOR_ACTION)
  yield put(START_ACTION)
  yield delay(100)
  yield take(channel)
  yield put(END_ACTION)
}

So putting this all together as non-pseudocode:

const {
  runSaga,
  stdChannel,
  effects: {
    take,
    put,
    actionChannel,
    delay
  }
} = window.ReduxSaga

const WAIT_FOR_ACTION = "WAIT_FOR_ACTION";
const START_ACTION = "START_ACTION";
const END_ACTION = "END_ACTION";

(async() => {
  const channel = stdChannel();
  const dispatched = [];
  const options = {
    dispatch: action => dispatched.push(action),
    getState: () => {},
    channel
  };
  const task = runSaga(options, sagaWorker);
  channel.put({
    type: WAIT_FOR_ACTION
  });
  await task.toPromise();
  console.log(dispatched);
})();

function* sagaWorker() {
  const channel = yield actionChannel(WAIT_FOR_ACTION);
  yield put({
    type: START_ACTION
  });
  yield delay(100);
  yield take(channel);
  yield put({
    type: END_ACTION
  });
}
<script src="https://cdn.jsdelivr/npm/[email protected]/dist/redux-saga.umd.min.js"></script>

发布评论

评论列表(0)

  1. 暂无评论