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

如何使用React Testing Library单元测试Redux Toolkit?求指导

Redux Toolkit 测试方案指导

关于测试范围的疑问

建议分开测试service层和slice层,同时补充组件集成测试:

  • 单独测试service:聚焦API调用逻辑(URL构造、请求参数、响应处理),能快速定位API相关问题。
  • 单独测试slice:聚焦状态更新逻辑(pending/fulfilled/rejected时的状态变化),确保状态流转符合预期。
  • 集成测试:验证组件、Redux、API的整体交互,模拟真实用户场景,保障功能端到端可用。

一、Service层测试(my.service.test.ts)

核心是mock外部依赖,验证fetchBeginningHeadcount的API调用逻辑:

import { fetchBeginningHeadcount, allHeadcountApiPaths } from './my.service';
import { api } from '../path-to-your-api-module';
import { ApiUrlService } from '../path-to-api-url-service';

// Mock外部依赖
jest.mock('../path-to-your-api-module', () => ({ api: jest.fn() }));
jest.mock('../path-to-api-url-service', () => ({
  ApiUrlService: { getTDS: jest.fn(() => 'mock-tds-url') }
}));

describe('fetchBeginningHeadcount', () => {
  const mockParams = { versionId: 'v1', page: 0, size: 10 };
  const mockResponse = { content: [], totalElements: 0, totalPages: 1 };

  beforeEach(() => jest.clearAllMocks());

  it('调用API时使用正确的方法和URL', async () => {
    (api as jest.Mock).mockResolvedValue({ data: mockResponse });
    await fetchBeginningHeadcount(mockParams);

    const expectedUrl = `${ApiUrlService.getTDS()}/rpc/v1/version/${mockParams.versionId}/planned-headcount/view/with-relations?page=${mockParams.page}&size=${mockParams.size}`;
    expect(api).toHaveBeenCalledWith('GET', expectedUrl, {});
  });

  it('API调用成功时返回正确数据', async () => {
    (api as jest.Mock).mockResolvedValue({ data: mockResponse });
    const result = await fetchBeginningHeadcount(mockParams);

    expect(result.payload).toEqual(mockResponse);
    expect(result.type).toBe('allHeadcount/fetchBeginningHeadcount/fulfilled');
  });

  it('API调用失败时返回错误信息', async () => {
    const mockError = new Error('API请求失败');
    (api as jest.Mock).mockRejectedValue(mockError);
    const result = await fetchBeginningHeadcount(mockParams);

    expect(result.error.message).toBe('API请求失败');
    expect(result.type).toBe('allHeadcount/fetchBeginningHeadcount/rejected');
  });
});

二、Slice层测试(my.slice.test.ts)

直接模拟action,验证状态更新逻辑,无需依赖API:

import allHeadcountReducer, { initialState } from './my.slice';
import { fetchBeginningHeadcount } from './my.service';

describe('allHeadcountSlice', () => {
  const mockData = [{ id: 1, name: '测试人员' }];
  const mockPayload = {
    content: mockData,
    totalElements: 1,
    totalPages: 1
  };

  it('pending状态下设置loading为true', () => {
    const nextState = allHeadcountReducer(
      initialState,
      fetchBeginningHeadcount.pending('req-id')
    );

    expect(nextState.beginningHeadcount.loading).toBe(true);
    expect(nextState.beginningHeadcount.error).toBeNull();
  });

  it('fulfilled状态下更新数据', () => {
    const nextState = allHeadcountReducer(
      initialState,
      fetchBeginningHeadcount.fulfilled(mockPayload, 'req-id')
    );

    expect(nextState.beginningHeadcount.loading).toBe(false);
    expect(nextState.beginningHeadcount.data).toEqual(mockData);
    expect(nextState.beginningHeadcount.total).toBe(1);
  });

  it('rejected状态下设置错误信息', () => {
    const errorMsg = '加载数据失败';
    const nextState = allHeadcountReducer(
      initialState,
      fetchBeginningHeadcount.rejected(new Error(errorMsg), 'req-id')
    );

    expect(nextState.beginningHeadcount.loading).toBe(false);
    expect(nextState.beginningHeadcount.error).toBe(errorMsg);
  });
});

三、组件集成测试(结合React Testing Library)

使用自定义的render函数,验证组件与Redux的交互:

import { render, screen, waitFor } from './my.provider';
import HeadcountComponent from './HeadcountComponent';
import { api } from '../path-to-your-api-module';

jest.mock('../path-to-your-api-module', () => ({ api: jest.fn() }));

describe('HeadcountComponent', () => {
  const mockParams = { versionId: 'v1' };
  const mockData = [{ id: 1, name: '张三' }];

  beforeEach(() => jest.clearAllMocks());

  it('加载时显示loading状态', async () => {
    // 延迟返回API响应,确保loading状态被捕获
    (api as jest.Mock).mockImplementation(() => 
      new Promise(resolve => setTimeout(() => resolve({ data: { content: mockData } }), 100))
    );

    render(<HeadcountComponent versionId={mockParams.versionId} />);
    expect(screen.getByText('加载中...')).toBeInTheDocument();
  });

  it('数据加载成功后显示内容', async () => {
    (api as jest.Mock).mockResolvedValue({ 
      data: { content: mockData, totalElements: 1, totalPages: 1 } 
    });

    render(<HeadcountComponent versionId={mockParams.versionId} />);
    await waitFor(() => expect(screen.getByText('张三')).toBeInTheDocument());
    expect(screen.queryByText('加载中...')).not.toBeInTheDocument();
  });

  it('加载失败时显示错误信息', async () => {
    (api as jest.Mock).mockRejectedValue(new Error('加载数据失败'));

    render(<HeadcountComponent versionId={mockParams.versionId} />);
    await waitFor(() => expect(screen.getByText('加载数据失败')).toBeInTheDocument());
    expect(screen.queryByText('加载中...')).not.toBeInTheDocument();
  });
});

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.21 19:54:25