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

Jest测试重置Redux Store触发RTK Query警告咨询

问题背景
  • Jest测试时直接复用项目生产Redux Store,API交互逻辑基于RTK Query实现,所有外部请求通过msw模拟。
  • 为避免测试用例间状态残留干扰结果,给Store新增RESET action,用于每个用例执行前重置全局状态。
  • 测试功能运行符合预期,但Jest持续输出警告:
Warning: Middleware for RTK-Query API at reducerPath "api" has not been added to the store.
Features like automatic cache collection, automatic refetching etc. will not be available.
  • 警告仅在调用RTK Query端点时触发,仅影响日志输出,不阻断测试流程。
  • 现有Store实现代码:
import { combineReducers, configureStore, AnyAction, Reducer } from "@reduxjs/toolkit";
import { FLUSH, PAUSE, PERSIST, persistReducer, persistStore, PURGE, REGISTER, REHYDRATE } from "redux-persist";
import storage from "redux-persist/lib/storage";

import { myApi } from "@/store/rtk/myApi";
import authSlice from "@/store/slices/authSlice";
import appSlice from "@/store/slices/appSlice";
import paginationSlice from "@/store/slices/paginationSlice";
import modalSlice from "@/store/slices/modalSlice";
import filesSlice from "@/store/slices/filesSlice";

const combinedReducer = combineReducers({
    [myApi.reducerPath]: myApi.reducer,
    app: appSlice,
    auth: authSlice,
    pagination: paginationSlice,
    modal: modalSlice,
    files: filesSlice,
});

// Create a rootReducer which will have additional actions.  In this case, "RESET" the entire store.
const rootReducer: Reducer = (state: RootState, action: AnyAction) => {
    if (action.type === "RESET") {
        storage.removeItem("persist:auth");

        // state = undefined;
        state = {} as RootState;
    }

    return combinedReducer(state, action);
};

const persistConfig = {
    key: "myapp",
    blacklist: [myApi.reducerPath, "app", "pagination", "modal", "files"],
    storage,
};

const store = configureStore({
    reducer: persistReducer(persistConfig, rootReducer),
    middleware: getDefaultMiddleware =>
        getDefaultMiddleware({
            serializableCheck: {
                ignoredActions: [FLUSH, REHYDRATE, PAUSE, PERSIST, PURGE, REGISTER],
            },
        }).concat([myApi.middleware]),
});

export const persistor = persistStore(store);

export default store;

// Infer the `RootState` and `AppDispatch` types from the store itself
export type RootState = ReturnType<typeof combinedReducer>;

// Inferred type: {posts: PostsState, comments: CommentsState, users: UsersState}
export type AppDispatch = typeof store.dispatch;
问题根因

警告来自RTK Query middleware的内置校验逻辑:middleware会持续检查store中对应reducerPath下的状态是否包含它注入的内部标识,检测不到就会抛出该提示。
当前RESET逻辑存在两处问题:

  • 将重置后的根状态直接设为空对象{} as RootState,绕过了Redux子reducer生成初始状态的标准流程,状态更新周期内会出现短暂的api slice结构不符合预期的情况
  • 重置时没有触发RTK Query内部的状态重置逻辑,middleware持有的内部缓存引用和store中实际状态不匹配
修复方案

方案1:修正RESET action的状态重置逻辑

Redux标准重置逻辑是给根reducer传入undefined,触发所有子reducer返回自身初始状态,不要手动构造空的根状态对象。修改rootReducer的RESET分支如下:

const rootReducer: Reducer = (state: RootState, action: AnyAction) => {
    if (action.type === "RESET") {
        storage.removeItem("persist:auth");
        // 传入undefined,让所有子reducer(包括RTK Query的api reducer)返回官方初始状态
        return combinedReducer(undefined as unknown as RootState, action);
    }
    return combinedReducer(state, action);
};

修改后RTK Query的api slice会回到标准初始结构,middleware可以正常识别到对应reducer,警告消失。

方案2:重置时额外调用RTK Query官方重置方法

如果因为redux-persist的rehydrate流程导致方案1仍有警告,可以在测试重置逻辑中,额外dispatch RTK Query提供的专用重置action,完整清空缓存、重建middleware内部状态:

// 测试用例的beforeEach重置逻辑中
store.dispatch({ type: "RESET" });
store.dispatch(myApi.util.resetApiState());
优化建议

测试场景下更稳妥的做法是每个测试用例单独创建全新的store实例,从根源上避免状态交叉污染,不需要给生产环境store额外增加RESET action。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.02 06:21:59