Reduxsauce实现的Redux Store单元测试方案及报错解决
我来帮你一步步解决这个测试报错的问题,并且梳理清楚Reduxsauce场景下的正确测试方式。
首先解决当前的报错问题
你现在的测试代码里,import * as actions from '../../../redux/LanguageRedux'会把文件里所有导出的内容(包括你定义的reducer函数getLanguage)都加载到actions对象里。当你调用actions.getLanguage()时,实际上调用的是reducer函数,而不是createActions生成的action creator。这就导致了错误——reducer函数期望接收state和action两个参数,但你调用时没传,所以解构{}的时候就会报Cannot destructure 'undefined' or 'null'。
正确的导入方式应该是导入默认导出的Creators(也就是action creators集合):
import actions from '../../../redux/LanguageRedux' import { LanguageTypes, INITIAL_STATE, reducer } from '../../../redux/LanguageRedux'
这样actions.getLanguage()和actions.setLanguage('zh')就会调用正确的action creator,而不是reducer函数。
接下来分模块编写测试
Reduxsauce的测试可以拆分为action creators测试、reducers测试、完整store测试三个部分,下面分别说明:
1. 测试Action Creators
我们需要验证action creators生成的action包含正确的type和payload:
describe('Language Action Creators', () => { it('should create an action to trigger getLanguage', () => { const expectedAction = { type: LanguageTypes.GET_LANGUAGE } expect(actions.getLanguage()).toEqual(expectedAction) }) it('should create an action to set language with correct payload', () => { const testLang = 'zh' const expectedAction = { type: LanguageTypes.SET_LANGUAGE, language: testLang } expect(actions.setLanguage(testLang)).toEqual(expectedAction) }) })
2. 测试Reducers
有两种方式测试reducers,你可以根据需求选择:
方式1:单独测试单个reducer函数
直接调用reducer函数,传入初始状态和模拟的action,检查返回的新状态:
describe('Language Reducer Functions', () => { it('getLanguage should return unchanged state', () => { const newState = getLanguage(INITIAL_STATE, {}) expect(newState).toEqual(INITIAL_STATE) }) it('setLanguage should update language state correctly', () => { const testLang = 'fr' const newState = setLanguage(INITIAL_STATE, { language: testLang }) expect(newState.language).toEqual(testLang) }) })
方式2:测试完整的reducer(推荐)
使用createReducer生成的完整reducer,传入不同的action,验证状态变化:
describe('Language Combined Reducer', () => { it('should return initial state when receiving unknown action', () => { expect(reducer(undefined, { type: 'UNKNOWN_ACTION' })).toEqual(INITIAL_STATE) }) it('should update language when handling SET_LANGUAGE action', () => { const testLang = 'de' const action = actions.setLanguage(testLang) const newState = reducer(INITIAL_STATE, action) expect(newState.language).toEqual(testLang) }) it('should keep state unchanged when handling GET_LANGUAGE action', () => { const action = actions.getLanguage() const newState = reducer(INITIAL_STATE, action) expect(newState).toEqual(INITIAL_STATE) }) })
3. 测试完整的Store
如果你想验证整个store的行为(包括reducer集成后的状态变化),可以创建测试store,dispatch动作后检查状态:
import configureStore from '../../../redux/CreateStore' import rootReducer from '../../../redux/rootReducer' // 对应你合并reducers的文件 describe('Language Store', () => { let store beforeEach(() => { // 创建测试用的store,如果不需要saga可以传null替代rootSaga store = configureStore(rootReducer, null) }) it('should initialize with default language "en"', () => { expect(store.getState().language.language).toEqual('en') }) it('should update language when SET_LANGUAGE is dispatched', () => { store.dispatch(actions.setLanguage('ja')) expect(store.getState().language.language).toEqual('ja') }) it('should not modify state when GET_LANGUAGE is dispatched', () => { const initialState = store.getState().language store.dispatch(actions.getLanguage()) expect(store.getState().language).toEqual(initialState) }) })
关键注意点
- 一定要区分action creators(
Creators对象里的方法)和reducer函数:导入时不要混淆,否则就会出现你遇到的调用错误。 - 测试reducer时,优先选择测试完整的
createReducer生成的reducer,这样能验证action type和reducer的绑定是否正确。 - 测试完整store时,要注意初始化store的方式和生产环境保持一致,确保测试结果和实际运行逻辑匹配。
内容的提问来源于stack exchange,提问作者Maverick

