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(); });
原因分析
- 回车添加测试失败原因
- 断言内容不匹配:输入的是"Second todo",但断言检查的是"First todo",完全不对应
- 全局Spy未重置:
updateTodo是全局定义的Spy,前一个测试的调用记录会残留到后续测试,导致断言结果异常
- 空输入回车测试失败原因
- 异步操作未等待:
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
相关产品推荐
相关产品推荐

