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

如何对redux-saga中的delay函数进行单元测试?

如何测试Redux-Saga中的delay?

首先,你遇到的测试失败问题,大概率是因为直接比较delay(1000)返回的对象引用或实例不相等——不管是Redux-Saga自带的delay(返回Effect对象)还是自定义的Promise版delay,每次调用都会生成新的对象,直接用toEqual断言自然会失败。下面分两种场景给出具体的解决方案:

场景1:使用Redux-Saga自带的delay(推荐)

Redux-Saga提供的delay是一个Effect创建器,返回的是特殊的Effect对象。测试这类Effect的最佳方式是使用官方的redux-saga/test-utils工具库,它提供了更简洁、可靠的测试API。

方法1:用expectSaga(最推荐,无需手动迭代Generator)

expectSaga可以直接测试Saga的完整流程,并且内置了断言delay的方法:

import { expectSaga } from 'redux-saga/test-utils';
import { delay } from 'redux-saga/effects';
import { incrementAsync } from './your-saga-file';

test('should trigger delay and increment', async () => {
  await expectSaga(incrementAsync)
    // 断言saga调用了1000ms的delay
    .toHaveCalledDelay(1000)
    // 运行saga并完成测试
    .run();
});

方法2:手动迭代Generator(用Effect匹配器)

如果你习惯手动遍历Generator,可以用Redux-Saga提供的matchers来断言Effect的类型和参数,而不是直接比较对象:

import { isDelay } from 'redux-saga/test-utils';
import { delay } from 'redux-saga/effects';
import { incrementAsync } from './your-saga-file';

test('should incrementAsync', () => {
  const generator = incrementAsync();
  
  // 断言第一个yield的是1000ms的delay Effect
  const delayEffect = generator.next().value;
  expect(isDelay(delayEffect, 1000)).toBe(true);
  
  // 继续后续的断言(比如触发increment action)
  // const incrementAction = generator.next().value;
  // expect(incrementAction).toEqual({ type: 'INCREMENT' });
});

场景2:自定义Promise版delay

如果你是自己实现的delay(比如返回Promise):

// 自定义delay
const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms));

这种情况下,每次调用delay(1000)都会生成新的Promise实例,直接用toEqual肯定不相等。此时可以通过以下方式测试:

方法1:用expectSaga模拟delay

expectSaga允许你mock掉Promise版的delay,避免真实等待,同时断言它被调用:

import { expectSaga } from 'redux-saga/test-utils';
import { incrementAsync } from './your-saga-file';
import * as delayModule from './where-delay-is-defined';

// Mock自定义的delay函数
jest.spyOn(delayModule, 'delay').mockResolvedValue();

test('should call custom delay and increment', async () => {
  await expectSaga(incrementAsync)
    .run();
  
  // 断言delay被调用了一次,参数是1000
  expect(delayModule.delay).toHaveBeenCalledWith(1000);
});

方法2:手动迭代Generator时断言Promise行为

如果你手动测试Generator,可以断言yield的值是Promise,并借助假计时器跳过真实等待:

import { incrementAsync } from './your-saga-file';

test('should incrementAsync', async () => {
  const generator = incrementAsync();
  const yieldedPromise = generator.next().value;
  
  // 断言返回的是Promise
  expect(yieldedPromise).toBeInstanceOf(Promise);
  
  // 用假计时器验证Promise的延迟行为
  jest.useFakeTimers();
  yieldedPromise.then(() => {
    const incrementAction = generator.next().value;
    expect(incrementAction).toEqual({ type: 'INCREMENT' });
  });
  jest.advanceTimersByTime(1000);
  jest.useRealTimers();
});

总结

最省心的方式是使用Redux-Saga官方的expectSaga工具,它不仅能轻松断言delay的调用,还能模拟整个Saga流程,避免手动处理Generator的繁琐操作。如果是自定义的Promise版delay,记得用Jest的mock功能拦截并断言调用,同时借助假计时器跳过真实等待,提升测试速度。

内容的提问来源于stack exchange,提问作者Dimitri Kopriwa

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 09:21:09