You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

redux-saga-test-plan测试:两次调用selector无法返回不同模拟值

Redux-Saga-Test-Plan 重复调用同一Selector的Mock响应问题

问题场景

编写Saga时两次调用同一个selector,使用redux-saga-test-plan的provide方法测试时,期望两次调用分别返回mockResponseOne和mockResponseTwo,但实际第二次调用始终返回第一次的mockResponseOne,无法获取预期值。

Saga代码示例

export function* mySaga(url) {
  // ...其他逻辑
  yield Effects.call(handleRequest, url);
  const responseOne = yield Effects.select(selectResponse);

  if (responseOne.status === "failed") {
    yield Effects.call(handleRequest, responseOne.url);
    const responseTwo = yield Effects.select(selectResponse);
    console.log(responseTwo) // 测试时此处接收的是responseOne
    // ...其他逻辑
  }
}

测试代码示例

import {expectSaga} from "redux-saga-test-plan";
import * as matchers from "redux-saga-test-plan/matchers";

it("should the console.log print mockResponseTwo", async () => {
  await expectSaga(
    mySaga,
    mockUrl,
  )
    .provide([
      [matchers.call.fn(handleRequest), mockResponseOne],
      [select(selectResponse), mockResponseOne],
      [matchers.call.fn(handleRequest), mockResponseTwo],
      [select(selectResponse), mockResponseTwo], // 此处响应未更新
    ])
    .call(handleRequest, mockUrl)
    .call(handleRequest, mockResponseOne.url)
});

问题原因

redux-saga-test-plan中,使用静态值的匹配器(如[select(selectResponse), mockResponseOne])时,每次匹配到该effect都会返回同一个静态值,重复添加相同的匹配器不会按顺序触发,无法实现多次调用返回不同值的需求。

解决方法

方法1:使用函数动态返回值

通过维护调用计数器,在函数中根据调用次数返回对应的值:

import {expectSaga} from "redux-saga-test-plan";
import * as matchers from "redux-saga-test-plan/matchers";
import {select} from 'redux-saga/effects';

it("should the console.log print mockResponseTwo", async () => {
  let selectInvokeCount = 0;
  
  await expectSaga(mySaga, mockUrl)
    .provide([
      // 根据handleRequest的调用参数返回对应结果
      [matchers.call.fn(handleRequest), (callEffect) => {
        return callEffect.args[0] === mockUrl ? mockResponseOne : mockResponseTwo;
      }],
      // 根据调用次数返回不同的selector结果
      [select(selectResponse), () => {
        selectInvokeCount++;
        return selectInvokeCount === 1 ? mockResponseOne : mockResponseTwo;
      }]
    ])
    .call(handleRequest, mockUrl)
    .call(handleRequest, mockResponseOne.url)
    .run();
});

方法2:使用matchers.sequence按顺序返回值

利用redux-saga-test-plan提供的sequence匹配器,指定按调用顺序返回的响应数组:

import {expectSaga} from "redux-saga-test-plan";
import * as matchers from "redux-saga-test-plan/matchers";
import {select} from 'redux-saga/effects';

it("should the console.log print mockResponseTwo", async () => {
  await expectSaga(mySaga, mockUrl)
    .provide([
      // 按顺序返回handleRequest的两次调用结果
      [matchers.call.fn(handleRequest), matchers.sequence([mockResponseOne, mockResponseTwo])],
      // 按顺序返回selector的两次调用结果
      [select(selectResponse), matchers.sequence([mockResponseOne, mockResponseTwo])]
    ])
    .call(handleRequest, mockUrl)
    .call(handleRequest, mockResponseOne.url)
    .run();
});

方法3:精确匹配调用上下文(可选)

如果需要更精细化控制,可以结合调用参数或上下文精确匹配每次effect:

import {expectSaga} from "redux-saga-test-plan";
import * as matchers from "redux-saga-test-plan/matchers";
import {select} from 'redux-saga/effects';

it("should the console.log print mockResponseTwo", async () => {
  await expectSaga(mySaga, mockUrl)
    .provide([
      // 第一次调用handleRequest返回mockResponseOne
      [matchers.call(handleRequest, mockUrl), mockResponseOne],
      // 第一次调用selector返回mockResponseOne
      [select(selectResponse), mockResponseOne],
      // 第二次调用handleRequest返回mockResponseTwo
      [matchers.call(handleRequest, mockResponseOne.url), mockResponseTwo],
      // 第二次调用selector返回mockResponseTwo
      [select(selectResponse), mockResponseTwo]
    ])
    .run();
});

注:此方法需确保每次effect的参数完全匹配,适用于调用参数明确的场景。

内容的提问来源于stack exchange,提问作者Mario Rodeghiero

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 08:06:51