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

React表单测试问题:Mock的第二个提交处理函数未被调用

React表单测试中loginFunction未被调用的问题

我在注册表单的提交处理函数中依次调用了registerFunction和loginFunction,但单元测试时只有前者的断言通过,后者始终失败。移除await loginFunction(e);后测试正常通过,说明提交函数已被执行,但loginFunction未被调用。同时,登录表单中对loginFunction的测试是正常的。

注册表单组件代码 (registerForm/index.js)

import { loginFunction, registerFunction } from "../../actions";
// registerform函数内部
const handleSubmit = async(e) => {
        e.preventDefault();
        setErrorMessage('')
        await registerFunction(e);
        await loginFunction(e);
        ...
}

注册表单测试代码 (registerForm/registerForm.test.js)

import RegisterForm from ".";
import { registerFunction, loginFunction } from "../../actions";
import 'react-router-dom'
import { fireEvent } from "@testing-library/react";

jest.mock('../../actions', () => ({
    loginFunction:jest.fn(),
    registerFunction:jest.fn(),
}))

describe('Unit test of registration form', function() {
    it('should register', () => {
        const {container} = render(< RegisterForm/>)
        fireEvent.submit(container.querySelector('.submit')) 
        expect(registerFunction).toBeCalledTimes(1) // 此断言通过
        expect(loginFunction).toBeCalledTimes(1) // 此断言失败
    })
})

登录表单对比代码

登录表单组件 (loginForm/index.js)

import { loginFunction } from "../../actions";
// loginform函数内部
const handleSubmit = async(e) => {
        e.preventDefault();
        setErrorMessage('')
        await loginFunction(e);
        ...
}

登录表单测试代码 (loginForm.test.js)

import LoginForm from ".";
import { loginFunction } from "../../actions"
import { fireEvent } from "@testing-library/react";

jest.mock('../../actions', () => ({
    loginFunction:jest.fn()
}))

describe('Unit test of login form', function() {
    it('should login', () => {
        const {container} = render(<LoginForm />)
        fireEvent.submit(container.querySelector('.submit'))
        expect(loginFunction).toBeCalledTimes(1) // 此断言通过
    })
})

问题原因及解决办法

核心原因

  1. 异步代码未等待完成:handleSubmit是异步函数,测试中触发提交后立刻执行断言,此时loginFunction可能还未被调用。登录表单只有一个异步调用,可能刚好在断言前执行完毕,但注册表单的两个异步调用存在时间差,导致断言失败。
  2. Mock函数未返回Promise:默认的jest.fn()返回undefined,await会将其包装为已决议的Promise,但如果后续逻辑依赖异步状态,可能导致执行流程异常。
  3. 组件提前卸载:若registerFunction执行后触发路由跳转(比如注册成功后跳转到首页),组件会被卸载,handleSubmit后续的loginFunction调用会被中断。

解决步骤

  1. 使用waitFor等待异步操作:借助Testing Library的waitFor工具,等待所有异步逻辑执行完成后再断言:
import { waitFor } from "@testing-library/react";

it('should register and trigger login', async () => {
    const {container} = render(<RegisterForm/>)
    fireEvent.submit(container.querySelector('.submit')) 
    await waitFor(() => {
        expect(registerFunction).toBeCalledTimes(1)
        expect(loginFunction).toBeCalledTimes(1)
    })
})
  1. 让Mock函数返回已决议的Promise:确保mock的异步函数返回Promise,符合真实函数的行为:
jest.mock('../../actions', () => ({
    loginFunction: jest.fn().mockResolvedValue({}),
    registerFunction: jest.fn().mockResolvedValue({}),
}))
  1. Mock路由跳转避免组件卸载:如果注册流程包含路由跳转,需要mockreact-router-dom的useNavigate方法,防止组件提前卸载:
import { useNavigate } from 'react-router-dom';

jest.mock('react-router-dom', () => ({
    ...jest.requireActual('react-router-dom'),
    useNavigate: jest.fn(),
}))

describe('Unit test of registration form', function() {
    it('should register and trigger login', async () => {
        const mockNavigate = jest.fn();
        jest.spyOn(require('react-router-dom'), 'useNavigate').mockReturnValue(mockNavigate);
        
        const {container} = render(<RegisterForm/>)
        fireEvent.submit(container.querySelector('.submit')) 
        await waitFor(() => {
            expect(registerFunction).toBeCalledTimes(1)
            expect(loginFunction).toBeCalledTimes(1)
        })
    })
})

内容的提问来源于stack exchange,提问作者Franklyn Asafo-Adjei

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 17:42:32