如何测试柯里化的Axios Redux Action是否被调用?
测试柯里化Redux Action的Jest模拟方案
问题场景
我有一个带柯里化dispatch的Redux Action,通过Axios发起请求,在React Native组件中用自定义hook调用该Action。编写测试代码时,mockDispatch的调用次数始终不符合预期,尝试多种模拟方式均未解决,需要明确如何用Jest正确模拟柯里化函数完成测试。
相关代码示例
1. Redux Action代码(./src/store/Auth/auth.action.ts)
import { Dispatch } from 'redux'; import axios from 'axios'; export default { getUsersData: () => (dispatch: Dispatch) => { return axios.get('https://jsonplaceholder.typicode.com/users').then((result) => { dispatch({ type: 'SET_USERS', payload: result }); return result; }) .catch((e) => { console.error(e); }); }, }
2. React Native组件代码(./src/screens/Testing/index.tsx)
import React, { useCallback } from 'react'; import { View, Button } from 'react-native'; import { useDispatch } from 'react-redux'; import Action from '../../store/Auth/auth.action'; const useAppDispatch = (action: any) => { const dispatch = useDispatch(); return useCallback((param?: any, callback?: any) => dispatch(action(param, callback)), [dispatch, action]); }; const Testing = () => { const getUsersDataAction = useAppDispatch(Action.getUsersData); return ( <View> <Button title='hit me' testID='hit_me_id' onPress={ () => getUsersDataAction() } /> </View> ); }; export default Testing;
3. 当前测试代码(action.test.js)
import * as React from 'react'; import * as ReactRedux from 'react-redux'; import axios from 'axios'; import { render, fireEvent, waitFor } from '@testing-library/react-native'; import { Testing } from '../../src/screens'; describe('Get users data', () => { it('should return correct payload', async () => { const mockDispatch = jest.fn(); jest.spyOn(ReactRedux, 'useDispatch').mockReturnValue(mockDispatch); axios.get.mockResolvedValue({}); const screen = render( <Testing /> ); await waitFor(() => { expect(screen.getByTestId('hit_me_id')).toBeTruthy(); }); await waitFor(() => { fireEvent.press(screen.getByTestId('hit_me_id')); }); expect(mockDispatch).toHaveBeenCalledTimes(1); expect(axios.get).toHaveBeenCalledWith('https://jsonplaceholder.typicode.com/users'); }); });
尝试过的无效模拟方式
const mockDispatch = () => jest.fn(); const mockDispatch = jest.fn(() => () => null); jest.spyOn(ReactRedux, 'useDispatch').mockReturnValue(mockDispatch()); jest.spyOn(ReactRedux, 'useDispatch').mockReturnValue(() => mockDispatch());
解决方案
问题核心在于Redux的dispatch本身支持传入函数(Thunk),会自动执行该函数并将自身作为参数传入,而之前的mockDispatch没有模拟这个行为,导致柯里化的Action函数未被执行,流程中断。
修正后的测试代码
import * as React from 'react'; import * as ReactRedux from 'react-redux'; import axios from 'axios'; import { render, fireEvent, waitFor } from '@testing-library/react-native'; import Testing from '../../src/screens/Testing'; describe('Get users data', () => { it('should return correct payload and dispatch action', async () => { // 模拟Redux dispatch的核心行为:执行传入的柯里化Action函数 const mockDispatch = jest.fn((actionOrFn) => { if (typeof actionOrFn === 'function') { return actionOrFn(mockDispatch); } return actionOrFn; }); jest.spyOn(ReactRedux, 'useDispatch').mockReturnValue(mockDispatch); // 模拟Axios返回的测试数据 const mockUsers = [{id: 1, name: 'Test User'}]; axios.get.mockResolvedValue(mockUsers); const screen = render(<Testing />); // 触发按钮点击 fireEvent.press(screen.getByTestId('hit_me_id')); // 等待异步流程完成后验证 await waitFor(() => { // 验证Axios请求地址正确 expect(axios.get).toHaveBeenCalledWith('https://jsonplaceholder.typicode.com/users'); // 验证dispatch被调用两次:一次传入柯里化函数,一次分发SET_USERS Action expect(mockDispatch).toHaveBeenCalledTimes(2); // 验证最终分发的Action内容正确 expect(mockDispatch).toHaveBeenCalledWith({ type: 'SET_USERS', payload: mockUsers }); }); }); });
关键说明
- 修正后的
mockDispatch会判断传入的参数类型:如果是函数(即柯里化的Action),就执行该函数并把自身作为参数传入,完全模拟Redux原生dispatch的行为。 - 此时
mockDispatch会被调用两次:第一次是组件中调用dispatch(action(param, callback)),传入的是getUsersData()返回的函数;第二次是在该函数内部调用dispatch({type: 'SET_USERS', ...}),传入的是Action对象。
内容的提问来源于stack exchange,提问作者fadly kayo
相关产品推荐
相关产品推荐

