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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 16:25:21