React Testing Library测试失败:表单提交未触发mock函数
React Testing Library测试失败排查:表单提交未触发onUserAdd
问题描述
我正在学习React Testing Library,开发了一个可添加姓名与邮箱至列表的简单应用,业务代码逻辑确认正常,但测试用例中验证表单提交触发onUserAdd的用例始终失败,请求排查原因。
组件代码
Home组件
"use client" import UserForm from './components/UserForm' import { useState } from 'react' import UserList from './components/UserList' export interface User{ name: string, email: string } export default function Home() { const [users, setUsers] = useState<User[]>([]) const onUserAdd = (user:User) => { setUsers([...users, user]) } return ( <> <h1>RTL App</h1> <UserForm onUserAdd={onUserAdd}/> <hr /> <UserList users={users}/> </> ) }
UserForm组件
"use client" import React, { useState } from 'react' const UserForm = ({ onUserAdd }: any) => { const [email, setEmail] = useState('') const [name, setName] = useState('') const handleSubmit = (e: React.FormEvent) => { e.preventDefault() onUserAdd({ name, email }) } return ( <form onSubmit={handleSubmit}> <div> <label htmlFor="name">Name</label> <input id="name" type="text" value={name} onChange={(e) => setName(e.target.value)} /> </div> <div> <label htmlFor="email">Email</label> <input id="email" type="text" value={email} onChange={(e) => setEmail(e.target.value)} /> </div> <button>Add User</button> </form> ) } export default UserForm
UserList组件
"use client" import React from 'react' const UserList = ({users}:any) => { const renderUsers = users.map((user:any) => { return( <tr key={user.name}> <td>{user.name}</td> <td>{user.email}</td> </tr> ) }) return ( <table> <thead> <tr> <th>Name</th> <th>Email</th> </tr> </thead> <tbody> {renderUsers} </tbody> </table> ) } export default UserList
测试用例代码
import { render, screen } from '@testing-library/react' import userEvent from '@testing-library/user-event' import UserForm from './UserForm' test('it show two inputs and a button', () => { render(<UserForm />); const inputs = screen.getAllByRole('textbox'); const button = screen.getByRole('button'); expect(inputs).toHaveLength(2); expect(button).toBeInTheDocument(); }); test('it calls onUserAdd when form submitted', () => { const mock = jest.fn(); render(<UserForm onUserAdd={mock} />); const nameInput = screen.getByRole('textbox', { name: /name/i }); const emailInput = screen.getByRole('textbox', { name: /email/i }); userEvent.type(nameInput, 'jane'); userEvent.type(emailInput, 'jane@jane.com'); const button = screen.getByRole('button'); userEvent.click(button); expect(mock).toHaveBeenCalled(); expect(mock).toHaveBeenCalledWith({ name: 'jane', email: 'jane@jane.com' }); });
测试结果与错误信息
测试结果:
✓ it show two inputs and a button (84 ms)
✕ it calls onUserAdd when form submitted (41 ms)
错误信息:
● it calls onUserAdd when form submitted expect(jest.fn()).toHaveBeenCalled() Expected number of calls: >= 1 Received number of calls: 0 23 | userEvent.click(button); 24 | > 25 | expect(mock).toHaveBeenCalled(); | ^ 26 | expect(mock).toHaveBeenCalledWith({ name: 'jane', email: 'jane@jane.com' }); 27 | }); 28 |
问题原因与解决方案
原因
userEvent v14+版本中,所有用户交互方法(如click、type)都改为异步方法,但测试中没有等待这些操作完成就执行断言,导致表单提交的handleSubmit逻辑还未执行,mock函数自然没有被调用。
解决方案
将测试函数改为异步函数,并用await等待所有用户交互操作完成,同时推荐使用userEvent.setup()创建用户实例(新版本最佳实践):
修改后的测试用例:
test('it calls onUserAdd when form submitted', async () => { const mock = jest.fn(); const user = userEvent.setup(); render(<UserForm onUserAdd={mock} />); const nameInput = screen.getByRole('textbox', { name: /name/i }); const emailInput = screen.getByRole('textbox', { name: /email/i }); await user.type(nameInput, 'jane'); await user.type(emailInput, 'jane@jane.com'); const button = screen.getByRole('button'); await user.click(button); expect(mock).toHaveBeenCalled(); expect(mock).toHaveBeenCalledWith({ name: 'jane', email: 'jane@jane.com' }); });
如果未使用setup,也可以直接await userEvent.click(button),但前者是新版本推荐的写法。
补充说明
如果是userEvent v13及以下版本,交互方法是同步的,但新版本已全面改为异步,这是这类测试失败的常见坑点。确保所有用户操作都用await等待,才能保证断言时相关业务逻辑已经执行完毕。
内容的提问来源于stack exchange,提问作者cdmt
相关产品推荐
相关产品推荐

