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) // 此断言通过 }) })
问题原因及解决办法
核心原因
- 异步代码未等待完成:
handleSubmit是异步函数,测试中触发提交后立刻执行断言,此时loginFunction可能还未被调用。登录表单只有一个异步调用,可能刚好在断言前执行完毕,但注册表单的两个异步调用存在时间差,导致断言失败。 - Mock函数未返回Promise:默认的
jest.fn()返回undefined,await会将其包装为已决议的Promise,但如果后续逻辑依赖异步状态,可能导致执行流程异常。 - 组件提前卸载:若
registerFunction执行后触发路由跳转(比如注册成功后跳转到首页),组件会被卸载,handleSubmit后续的loginFunction调用会被中断。
解决步骤
- 使用
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) }) })
- 让Mock函数返回已决议的Promise:确保mock的异步函数返回Promise,符合真实函数的行为:
jest.mock('../../actions', () => ({ loginFunction: jest.fn().mockResolvedValue({}), registerFunction: jest.fn().mockResolvedValue({}), }))
- Mock路由跳转避免组件卸载:如果注册流程包含路由跳转,需要mock
react-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
相关产品推荐
相关产品推荐

