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

Redux Toolkit+Enzyme/Jest测试求助:Mock Store创建及多组件测试

解决Redux Toolkit组件测试的状态污染与Mock Store问题

首先,咱们先搞定你遇到的测试用例状态共享核心问题:你当前测试用的是全局store实例,所有测试用例会共享这个状态,所以递增测试修改状态后,递减测试拿到的是修改后的状态,自然断言失败。咱们需要给每个测试用例创建独立、干净的Store实例。

第一步:修改Root组件,支持传入自定义Store

把你的Root组件改成可以接受可选的store属性,这样测试时能传入临时创建的Store,而非全局默认的那个:

import React from 'react';
import { Provider } from 'react-redux';
import store from 'store/store'; // 全局默认store
import { createGlobalStyle } from 'styled-components';

const GlobalStyle = createGlobalStyle`
 body {
 background-color: #282c34;
 color: white;
 font-family: "Lucida Console", Courier, monospace;
 }
`;

interface IProps {
 children: JSX.Element[] | JSX.Element;
 store?: typeof store; // 添加可选的store属性
}

export default ({ children, store: customStore = store }: IProps) => {
 return (
 <Provider store={customStore}>
 <GlobalStyle />
 {children}
 </Provider>
 );
};

第二步:在测试中创建独立的测试Store

Redux Toolkit的configureStore本身就可以用来创建测试用Store,不需要额外依赖redux-mock-store。咱们在beforeEach里每次都新建一个Store实例,确保每个测试都是干净的初始状态:

修改你的计数器测试用例:

import React from 'react';
import { mount } from "enzyme";
import Counter from 'components/counter';
import Root from "root/index";
import { configureStore } from '@reduxjs/toolkit';
import rootReducer from 'store/combineReducer'; // 导入根reducer

let wrapped;
let testStore;

beforeEach(() => {
 // 每次测试都创建新Store,初始状态与生产环境一致
 testStore = configureStore({
  reducer: rootReducer
 });
 wrapped = mount(
 <Root store={testStore}>
 <Counter />
 </Root>
 );
});

afterEach(() => {
 wrapped.unmount();
});

describe(`Counter Component Tests`, () => {
 it(`should render initial counter value as 1`, () => {
 expect(wrapped.find(`h1`).text()).toEqual(`1`);
 });

 it(`should increment counter to 2 when increment button is clicked`, () => {
 wrapped.find(`[data-test="increment"]`).simulate(`click`);
 // Enzyme的mount组件不会自动更新,需手动调用update()获取最新DOM
 wrapped.update();
 expect(wrapped.find(`h1`).text()).toEqual(`2`);
 });

 it(`should decrement counter to 0 when decrement button is clicked`, () => {
 wrapped.find(`[data-test="decrement"]`).simulate(`click`);
 wrapped.update();
 // 你原来的断言写的是toEqual('2'),这里修正为正确的预期值'0'
 expect(wrapped.find(`h1`).text()).toEqual(`0`);
 });

 // 测试特定初始状态的场景:比如初始值为5
 it(`should decrement from 5 to 4 when button is clicked`, () => {
 // 创建带自定义初始状态的Store
 const customStore = configureStore({
  reducer: rootReducer,
  preloadedState: {
   counter: 5 // 指定counter的初始状态
  }
 });
 const customWrapped = mount(
 <Root store={customStore}>
 <Counter />
 </Root>
 );
 customWrapped.find(`[data-test="decrement"]`).simulate(`click`);
 customWrapped.update();
 expect(customWrapped.find(`h1`).text()).toEqual(`4`);
 customWrapped.unmount();
 });
});

关键说明:

  • 每次beforeEach调用configureStore创建新Store,确保测试完全隔离。
  • 通过preloadedState可以轻松指定任意初始状态,覆盖不同测试场景。
  • Enzyme的mount组件在Redux状态变化后不会自动更新,必须调用wrapped.update()才能获取最新DOM。

第三步:其他组件的测试思路

1. 待办事项(Todo)组件测试

Todo组件的测试逻辑和计数器类似:

  • 每次创建干净的Store,可通过preloadedState传入预设的待办列表。
  • 测试添加/删除/修改Todo的操作,模拟用户交互后更新组件,断言DOM变化和Store状态是否符合预期。
  • 如果Todo组件涉及异步操作(比如保存到后端),可以用Jest模拟API请求,或者在测试Store中添加中间件捕获异步action。

2. 数据获取(Fetch User)组件测试

对于带异步数据获取的组件(比如用createAsyncThunk的场景):

  • 用Jest模拟API请求函数,示例:
    jest.mock('your-api-module', () => ({
     fetchUser: jest.fn(() => Promise.resolve({ id: 1, name: 'Test User' }))
    }));
    
  • Redux Toolkit的getDefaultMiddleware默认包含redux-thunk,所以测试Store可以直接处理异步action。
  • 测试步骤:
    1. 挂载组件,触发数据获取操作(比如组件挂载时自动请求)。
    2. 用async/await配合wrapped.update()等待Promise完成。
    3. 断言DOM是否渲染了获取到的数据,或Store中的状态是否正确。

额外提示:单独测试Reducer/Action

如果不需要挂载组件,只想测试Reducer或Action的逻辑,可以直接调用Reducer函数,传入初始状态和Action,断言新状态是否正确:

import counterReducer, { increment, decrement } from 'store/reducer/counter';

test('increment action should increase state by 1', () => {
 const initialState = 1;
 const newState = counterReducer(initialState, increment());
 expect(newState).toBe(2);
});

test('decrement action should decrease state by 1', () => {
 const initialState = 1;
 const newState = counterReducer(initialState, decrement());
 expect(newState).toBe(0);
});

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.08 17:47:51