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

Vitest搭配React Testing Library时userEvent.click无法触发表单提交

问题背景

使用开箱即用的Create React App时,以下测试可正常运行并通过:

import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import App from "./App";

describe("Update the name", () => {
  it("updates the name", () => {
    render(<App />);
    userEvent.type(screen.getByLabelText(/name/i), "John");
    userEvent.click(screen.getByRole("button"));

    expect(screen.getByText("John")).toBeInTheDocument();
  });
});

上述测试逻辑本身无误:页面仅存在1个设置了type="submit"的按钮,应用在浏览器中运行正常,在Create React App(CRA)环境下测试可顺利通过。

迁移至基于Vite的Vitest测试框架时,理论上无需修改上述测试代码,但实际运行出现报错:TypeError: target.ownerDocument.createRange is not a function。

现有配置

vite.config.js配置内容为:

/// <reference types="vitest" />
/// <reference types="vite/client" />

import react from "@vitejs/plugin-react";
import { defineConfig } from "vite";

export default defineConfig({
  plugins: [react()],
  test: {
    globals: true,
    environment: "happy-dom",
    setupFiles: "./src/setup.js",
  },
});

./src/setup.js仅包含一行代码:import "happy-dom";

已尝试的排查方案:

  • 切换测试环境为jsdom
  • 将userEvent.click替换为fireEvent.click
  • 给表单添加data-testid属性后直接调用fireEvent.submit触发表单提交

后几种方案运行时无报错,但要么完全未触发表单提交逻辑,要么提交触发后组件渲染未按预期更新。
由于相同测试逻辑在CRA中可正常运行,测试代码本身无需修改,问题出在配置层面。

解决方案

问题由两个核心配置/版本兼容问题导致,按以下步骤修正即可正常运行:

  1. 修正环境配置错误
    移除./src/setup.js中的import "happy-dom";代码。当vitest配置中已指定environment: "happy-dom"时,vitest会自动完成测试环境的初始化与DOM上下文注入,手动导入happy-dom会造成DOM实例重复、上下文不匹配,是触发createRange is not a function报错的核心原因。
    如果需要切换到jsdom环境,需先单独安装jsdom依赖:npm install -D jsdom,vitest 0.23及以上版本不再内置jsdom,未安装直接切换环境会导致DOM API缺失,出现事件不触发、渲染不更新的问题。

  2. 适配@testing-library/user-event v14+ 异步API
    CRA默认集成的user-event为v13及以下同步版本,Vite生态下默认安装的是v14+版本,该版本所有交互API均为异步实现,必须配合async/await调用,否则事件队列不会按预期执行,就会出现点击、输入后提交不触发、渲染不更新的问题。修正后的测试代码如下:

import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import App from "./App";

describe("Update the name", () => {
  it("updates the name", async () => {
    const user = userEvent.setup();
    render(<App />);
    await user.type(screen.getByLabelText(/name/i), "John");
    await user.click(screen.getByRole("button"));

    expect(screen.getByText("John")).toBeInTheDocument();
  });
});
  1. (可选)补全happy-dom缺失API补丁
    如果使用happy-dom时仍存在API缺失问题,可以在setup.js中添加createRange方法的polyfill:
if (!window.document.createRange) {
  window.document.createRange = () => ({
    setStart: () => {},
    setEnd: () => {},
    commonAncestorContainer: {
      nodeName: "BODY",
      ownerDocument: document,
    },
  });
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 18:54:23