React Testing Library测试失败:提交对象值为空问题排查
React Testing Library测试UserForm组件问题解决
问题概述
测试UserForm组件时,模拟表单提交后onUserAdd接收到的name和email为空值,而非预期的输入内容;同时咨询测试中给每个事件添加await的用法是否正确。
待测试组件代码
import { useState } from "react"; const UserForm = ({ onUserAdd }) => { const [name, setName] = useState(""); const [email, setEmail] = useState(""); const handleSubmit = (e) => { e.preventDefault(); onUserAdd({ name, email }); }; return ( <form onSubmit={handleSubmit}> <div> <label>Name</label> <input value={name} onChange={(e) => setName(e.target.value)} /> </div> <div> <label>Email</label> <input value={email} onChange={(e) => setEmail(e.target.value)} /> </div> <button onClick={handleSubmit}>Add User</button> </form> ); }; export default UserForm;
失败的测试用例
test("it calls onUserAdd when the form is submitted", async () => { // BETTER IMPLEMENTATION const mock = jest.fn(); // Render - the component render(<UserForm onUserAdd={mock} />); // Find 2 inputs (name, email in this case) const [nameInput, emailInput] = screen.getAllByRole("textbox"); // Simulate typing in a name await user.click(nameInput); await user.keyboard("jane"); // Simulate typing in a email await user.click(emailInput); await user.keyboard("abc@abc.com"); // Find the submit button const button = screen.getByRole("button"); // Simulate clicking the submit button await user.click(button); // Assertion - make sure 'onUserAdd' gets called with name/email expect(mock).toHaveBeenCalled(); expect(mock).toHaveBeenCalledWith({ name: "jane", email: "abc@abc.com" }); });
错误信息
- Expected + Received Object { - "email": "abc@abc.com", - "name": "jane", + "email": "", + "name": "", },
问题解答
1. 提交后值为空的原因及修复
核心问题
组件中<button>同时绑定了onClick={handleSubmit},且表单本身也绑定了onSubmit={handleSubmit}。当点击按钮时,会触发两次handleSubmit:
- 第一次是按钮的
onClick事件,传入的e是鼠标点击事件,调用e.preventDefault()会阻止后续表单的默认提交行为; - 测试中使用
user.keyboard模拟输入的方式不够可靠,它仅模拟单个按键序列,若未正确同步,可能无法触发input的onChange事件,导致状态未更新。
修复步骤
步骤1:修正组件提交逻辑
移除按钮上的onClick绑定,让按钮作为表单默认提交按钮(原生<button>在<form>内默认type="submit"),仅保留表单的onSubmit事件:
// 组件中修改button部分 <button>Add User</button>
步骤2:优化测试输入模拟
使用user.type替代click + keyboard组合,user.type是@testing-library/user-event专门用于文本输入的方法,会自动聚焦输入框并触发正确的onChange事件,更可靠:
// 替换测试中的输入代码 // 模拟输入name await user.type(nameInput, "jane"); // 模拟输入email await user.type(emailInput, "abc@abc.com");
可选:直接触发表单提交
也可以跳过点击按钮,直接触发表单的提交事件:
await user.submit(screen.getByRole("form"));
2. 关于await的用法
在@testing-library/user-event中,绝大多数交互方法(如click、type、keyboard、submit)都是异步方法,因为它们会触发React状态更新、DOM重渲染等异步操作。添加await可以确保当前交互完成(状态更新、DOM变化生效)后再执行后续代码,这种用法是正确且推荐的。
省略await可能导致测试代码在交互未完成时就执行断言,出现状态未同步的错误。
内容的提问来源于stack exchange,提问作者Nesh
相关产品推荐
相关产品推荐

