使用Jest测试Redux Reducer时遭遇TypeError: expect(...).arrayContaining is not a function错误求修复
Jest测试Redux Reducer时遭遇TypeError: expect(...).arrayContaining is not a function错误求修复
嘿,我来帮你搞定这个问题!你现在遇到了两个核心问题:Jest匹配器语法用错了,还有Reducer本身的逻辑bug,咱们一步步来修复:
首先解决TypeError的根源:Jest匹配器用法错误
你之所以会碰到TypeError: expect(...).arrayContaining is not a function,是因为arrayContaining是Jest的期望匹配器,它不能直接链式调用在expect()后面,必须作为参数传给toEqual、toContainEqual这类断言方法。而且它的参数必须是一个数组,不是多个零散的对象。
举个错误和正确用法的对比:
// ❌ 错误用法 expect(mockStore.getState()).arrayContaining(obj1, obj2) // ✅ 正确用法 expect(mockStore.getState()).toEqual(expect.arrayContaining([obj1, obj2]))
然后修复测试用例的逻辑错误
你写的第二个测试用例,目标是验证FETCH_COURSE_SUCCESS触发后的状态,但你却dispatch了SELECT_COURSE的action——这完全不匹配,肯定得不到你想要的结果!
最后修复Reducer的核心逻辑bug
你的Reducer在处理SELECT_COURSE和UNSELECT_COURSE时犯了一个关键错误:它每次都把原始的data数组重新映射后追加到state里,而不是基于当前已有的state去修改对应课程的选中状态。这会导致state里的课程不断重复累加,选中状态也不会正确更新。
修复后的完整代码
1. 修复后的测试文件(myJestCode.js)
import { courseReducer } from "./courseReducer"; import { configureStore } from '@reduxjs/toolkit'; import { SELECT_COURSE, UNSELECT_COURSE, FETCH_COURSE_SUCCESS } from './courseActionTypes'; describe('course reducer', () => { // 这个测试用例没问题,保留 it('returns an empty array as default state', () => { const store = configureStore({ reducer: courseReducer }); expect(store.getState()).toEqual([]); }); // 修复后的FETCH测试用例 it('should return course array with isSelected set to false after FETCH_COURSE_SUCCESS', () => { const mockStore = configureStore({ reducer: courseReducer }); // 👉 这里要dispatch正确的action类型!之前错用了SELECT_COURSE mockStore.dispatch({ type: FETCH_COURSE_SUCCESS }); // 👉 正确使用arrayContaining匹配器 expect(mockStore.getState()).toEqual(expect.arrayContaining([ { id: 1, name: 'ES6', credit: 60, isSelected: false }, { id: 2, name: 'Webpack', credit: 20, isSelected: false }, { id: 3, name: 'React', credit: 40, isSelected: false } ])); // 如果需要严格匹配数组的长度和顺序,可以直接用toEqual([...]) }); // 新增一个测试SELECT_COURSE的用例,验证修复后的Reducer逻辑 it('should set isSelected to true for course id 1 when SELECT_COURSE is dispatched', () => { const mockStore = configureStore({ reducer: courseReducer }); // 先获取课程数据 mockStore.dispatch({ type: FETCH_COURSE_SUCCESS }); // 再触发选中操作(注意action里的index对应课程id) mockStore.dispatch({ type: SELECT_COURSE, index: 1 }); expect(mockStore.getState()).toEqual(expect.arrayContaining([ { id: 1, name: 'ES6', credit: 60, isSelected: true }, { id: 2, name: 'Webpack', credit: 20, isSelected: false }, { id: 3, name: 'React', credit: 40, isSelected: false } ])); }); });
2. 修复后的Reducer文件(courseReducer.js)
import { configureStore } from '@reduxjs/toolkit'; import { SELECT_COURSE, UNSELECT_COURSE, FETCH_COURSE_SUCCESS } from './courseActionTypes'; // 测试用的课程数据 const data = [ { id: 1, name: "ES6", credit: 60 }, { id: 2, name: "Webpack", credit: 20 }, { id: 3, name: "React", credit: 40 } ]; export const courseReducer = (state = [], action) => { switch(action.type) { case FETCH_COURSE_SUCCESS: // 避免重复添加数据:如果state已有内容,直接返回;否则添加带isSelected的课程 if (state.length > 0) return state; return data.map(course => ({ ...course, isSelected: false })); case SELECT_COURSE: // 👉 遍历当前state,修改对应id的课程的选中状态 return state.map(course => course.id === action.index ? { ...course, isSelected: true } : course ); case UNSELECT_COURSE: // 👉 同理,遍历当前state修改选中状态 return state.map(course => course.id === action.index ? { ...course, isSelected: false } : course ); default: return state; } }; // 本地测试用的store const store = configureStore({ reducer: courseReducer }); console.log('Initial state:', store.getState()); store.dispatch({ type: FETCH_COURSE_SUCCESS }); console.log('After FETCH:', store.getState()); store.dispatch({ type: SELECT_COURSE, index: 1 }); console.log('After SELECT id 1:', store.getState());
为什么这些修改能解决问题?
- Jest语法修正:把
arrayContaining放在toEqual内部,符合Jest匹配器的正确调用方式。 - 测试用例逻辑修正:dispatch了正确的
FETCH_COURSE_SUCCESSaction,确保测试场景和目标一致。 - Reducer逻辑修正:处理SELECT/UNSELECT时,基于当前state进行修改,而不是重复追加原始数据,保证状态更新的正确性。
备注:内容来源于stack exchange,提问作者Moez Ben Rebah
相关产品推荐
相关产品推荐

