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

Testing Library测试输入元素回车逻辑时出现错误求助

待办添加组件测试问题排查与解决

问题描述

  • 输入内容后点击按钮添加至待办列表:测试正常通过
  • 输入内容后按下回车键触发添加:测试失败
  • 空输入时按下回车键:测试未通过,报错信息:AssertionError: expected "spy" to not be called at all, but actually been called 1 times

原始测试代码

基础测试代码

import { describe, vi, it, expect } from "vitest";
import { AddTodo } from "./AddItem";
import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";

const updateTodo = vi.fn();

describe("Add Item component", () => {
  it("should contain a input with button element", () => {
    render(<AddTodo updateTodo={updateTodo} />);
    expect(screen.getByRole("textbox")).toBeInTheDocument();
    expect(screen.getByRole("button")).toBeInTheDocument();
  });
  it("should add a todo item into screen", async () => {
    render(<AddTodo updateTodo={updateTodo} />);
    const inputBox = await screen.findByRole("textbox");
    const button = await screen.findByRole("button", { name: "+" });
    await userEvent.type(inputBox, "First todo");
    expect(inputBox).toHaveValue("First todo");
    await userEvent.click(button);
    expect(updateTodo).toHaveBeenCalledWith(
      expect.objectContaining({ text: "First todo" })
    );
  });
  it("should add a todo item into screen on enter", async () => { //fails
    render(<AddTodo updateTodo={updateTodo} />);
    const inputBox = await screen.findByTestId("todo-input");
    await userEvent.type(inputBox, "Second todo");
    await userEvent.keyboard("{enter}");
    expect(updateTodo).toHaveBeenCalledWith(
      expect.objectContaining({ text: "First todo" })
    );
  });
});

空输入失败测试用例

it("should not call the updateTodo when input not provided", async () => {
    const user = userEvent.setup();
    const { findByTestId } = render(<AddTodo updateTodo={updateTodo} />);
    const inputBox = await findByTestId("todo-input");
    user.type(inputBox, "");
    await user.keyboard("{enter}");
    expect(updateTodo).not.toHaveBeenCalled();
  });

原因分析

  1. 回车添加测试失败原因
    • 断言内容不匹配:输入的是"Second todo",但断言检查的是"First todo",完全不对应
    • 全局Spy未重置:updateTodo是全局定义的Spy,前一个测试的调用记录会残留到后续测试,导致断言结果异常
  2. 空输入回车测试失败原因
    • 异步操作未等待:user.type(inputBox, "")没有加await,输入操作未完成就触发回车,可能导致组件读取到旧值
    • 全局Spy未重置:同样受前序测试的调用记录影响
    • 组件逻辑缺失:AddTodo组件可能未在回车事件中判断输入内容是否为空,导致空值时依然调用了updateTodo

解决方案

1. 重置全局Spy

在describe块内添加beforeEach钩子,确保每个测试前清空Spy的调用记录:

describe("Add Item component", () => {
  beforeEach(() => {
    vi.clearAllMocks(); // 重置所有Spy的调用记录
  });
  // ... 其他测试用例
});

2. 修正回车添加测试的断言内容

把断言里的"First todo"改为输入的"Second todo":

it("should add a todo item into screen on enter", async () => {
    render(<AddTodo updateTodo={updateTodo} />);
    const inputBox = await screen.findByTestId("todo-input");
    await userEvent.type(inputBox, "Second todo");
    await userEvent.keyboard("{enter}");
    expect(updateTodo).toHaveBeenCalledWith(
      expect.objectContaining({ text: "Second todo" }) // 修正为对应输入内容
    );
  });

3. 补全空输入测试的异步等待

给user.type添加await,确保输入操作完成:

it("should not call the updateTodo when input not provided", async () => {
    const user = userEvent.setup();
    const { findByTestId } = render(<AddTodo updateTodo={updateTodo} />);
    const inputBox = await findByTestId("todo-input");
    await user.type(inputBox, ""); // 添加await
    await user.keyboard("{enter}");
    expect(updateTodo).not.toHaveBeenCalled();
  });

4. 修复组件的回车事件逻辑

确保AddTodo组件在回车时先判断输入内容是否为空,非空才调用updateTodo。示例组件逻辑:

// AddItem.tsx 示例修正逻辑
import { useState } from 'react';

export const AddTodo = ({ updateTodo }) => {
  const [inputText, setInputText] = useState('');

  const handleAdd = () => {
    if (inputText.trim()) { // 非空判断
      updateTodo({ text: inputText });
      setInputText('');
    }
  };

  const handleKeyDown = (e) => {
    if (e.key === 'Enter') {
      handleAdd(); // 复用添加逻辑,自动包含非空判断
    }
  };

  return (
    <div>
      <input
        type="text"
        value={inputText}
        onChange={(e) => setInputText(e.target.value)}
        onKeyDown={handleKeyDown}
        data-testid="todo-input"
      />
      <button onClick={handleAdd}>+</button>
    </div>
  );
};

内容的提问来源于stack exchange,提问作者3gwebtrain

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 04:04:59