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

使用Vitest测试React Redux Thunk注册函数时断言错误排查

解决Redux Thunk注册功能测试的AssertionError问题

问题描述

测试基于Redux的React应用时遇到错误:AssertionError: expected "spy" to be called with arguments: [ [Function] ],错误出现在验证注册成功场景的Action派发逻辑时。相关代码如下:

action.js

function asyncRegisterUser({ name, email, password }) {
    return async (dispatch) => {
        dispatch(showLoading());
        try {
            await api.register({ name, email, password });
        } catch (error) {
            alert(error.message);
        }
        dispatch(hideLoading());
    };
}

export {
    asyncRegisterUser,
};

action.test.js

import { hideLoading, showLoading } from 'react-redux-loading-bar';
import {
 afterEach, beforeEach, describe, expect, it, vi,
} from 'vitest';
import api from '../../utils/api';
import { asyncRegisterUser } from './action';

const fakeRegisterResponse = {
    id: 'john_doe',
    name: 'John Doe',
    email: 'john@example.com',
    avatar: 'https://generated-image-url.jpg',
};

const fakeRegisterInput = {
    name: 'John Doe',
    email: 'john@example.com',
    password: 'JohnDoe',
};

const fakeErrorResponse = new Error('Ups, something went wrong');

describe('asyncRegisterUser thunk', () => {
    beforeEach(() => {
        api._registerUser = api.register;
    });

    afterEach(() => {
        api.register = api._registerUser;

        delete api._registerUser;
    });

    it('should dispatch action correctly when register success', async () => {
        api.register = () => Promise.resolve(fakeRegisterResponse);
        const dispatch = vi.fn();

        await asyncRegisterUser(fakeRegisterInput)(dispatch);

        expect(dispatch).toHaveBeenCalledWith(showLoading());
        expect(dispatch).toHaveBeenCalledWith(
            asyncRegisterUser(fakeRegisterInput), // ERROR HERE
        );
        expect(dispatch).toHaveBeenCalledWith(hideLoading());
    });

    it('should dispatch action and call alert correctly when register failed', async () => {
        api.register = () => Promise.reject(fakeErrorResponse);

        const dispatch = vi.fn();

        window.alert = vi.fn();

        await asyncRegisterUser(fakeRegisterInput)(dispatch);

        expect(dispatch).toHaveBeenCalledWith(showLoading());
        expect(dispatch).toHaveBeenCalledWith(hideLoading());
        expect(window.alert).toHaveBeenCalledWith(fakeErrorResponse.message);
    });
});

错误原因

测试代码中错误地断言dispatch会调用asyncRegisterUser(fakeRegisterInput),但实际上在asyncRegisterUser这个thunk函数内部,并没有派发自身作为action,它只在注册流程前后分别派发了showLoading()和hideLoading()。这行多余的断言导致匹配失败,因为asyncRegisterUser返回的是一个函数,和实际dispatch的参数不匹配。

修正后的测试代码

移除错误的断言行即可,修正后的注册成功场景测试如下:

it('should dispatch action correctly when register success', async () => {
    api.register = () => Promise.resolve(fakeRegisterResponse);
    const dispatch = vi.fn();

    await asyncRegisterUser(fakeRegisterInput)(dispatch);

    // 只验证实际被派发的action
    expect(dispatch).toHaveBeenCalledWith(showLoading());
    expect(dispatch).toHaveBeenCalledWith(hideLoading());
    // 如果后续注册成功后需要派发其他action(比如registerSuccess),再添加对应断言
});

如果你的业务逻辑中,注册成功后本应派发一个额外的成功action(比如registerSuccess(fakeRegisterResponse)),需要先在action.js中补充这个逻辑:

补充后的action.js

// 先定义成功action类型和创建函数
const REGISTER_SUCCESS = 'REGISTER_SUCCESS';

function registerSuccess(user) {
    return {
        type: REGISTER_SUCCESS,
        payload: {
            user,
        },
    };
}

function asyncRegisterUser({ name, email, password }) {
    return async (dispatch) => {
        dispatch(showLoading());
        try {
            const user = await api.register({ name, email, password });
            dispatch(registerSuccess(user)); // 添加成功action派发
        } catch (error) {
            alert(error.message);
        }
        dispatch(hideLoading());
    };
}

export {
    asyncRegisterUser,
    registerSuccess,
};

对应的测试代码也要添加该action的断言:

// 导入registerSuccess
import { asyncRegisterUser, registerSuccess } from './action';

// 注册成功测试用例
it('should dispatch action correctly when register success', async () => {
    api.register = () => Promise.resolve(fakeRegisterResponse);
    const dispatch = vi.fn();

    await asyncRegisterUser(fakeRegisterInput)(dispatch);

    expect(dispatch).toHaveBeenCalledWith(showLoading());
    expect(dispatch).toHaveBeenCalledWith(registerSuccess(fakeRegisterResponse));
    expect(dispatch).toHaveBeenCalledWith(hideLoading());
});

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 14:47:02