使用 vitest 和 React 测试 `window.onerror` 调用

问题描述 投票:0回答:1

当抛出事件处理程序时,我无法在使用 vitest、jsdom 和 React 测试库的 React 测试中达到

window.onerror
。在 Jest 中也通过了相同的测试。

验证码

import { render, screen } from "@testing-library/react";
import ReactTestUtils from "react-dom/test-utils";
import { vi, it, expect } from "vitest";
import React from "react";

it("window.onerror is called on unhandled error in event handler", async () => {
  const spy = vi.spyOn(console, "error");
  spy.mockImplementation(() => undefined);

  const caught = vi.fn();
  const App = () => {
    return (
      <button
        onClick={() => {
          throw new Error("ahhhh");
        }}
      >
        Error
      </button>
    );
  };

  class Tracker extends React.Component<any, any> {
    componentDidMount() {
      window.onerror = (message, source, lineno, colno, error) => {
        caught();
        return true;
      };
    }

    render() {
      return this.props.children;
    }
  }

  render(
    <Tracker>
      <App />
    </Tracker>
  );

  expect(caught).toHaveBeenCalledTimes(0);
  const button = await screen.findByText("Error");

  try {
    // using ReactTestUtils here since it allows me to catch the error
    ReactTestUtils.Simulate.click(button);
  } catch (e) {
    // do nothing
  }

  expect(caught).toHaveBeenCalledTimes(1);
});

依赖关系

    "react": "^18.2.0",
    "react-dom": "^18.2.0"

    "@testing-library/react": "^14.1.2",
    "@vitejs/plugin-react": "^4.2.0",
    "jsdom": "^23.0.1",
    "vite": "^5.0.4",
    "vitest": "^0.34.6"

测试失败并显示以下错误消息:

 FAIL  index.test.tsx > window.onerror is called on unhandled error in event handler
AssertionError: expected "spy" to be called 1 times, but got 0 times
 ❯ index.test.tsx:52:18
     50|   }
     51| 
     52|   expect(caught).toHaveBeenCalledTimes(1);
       |                  ^
     53| });
     54| 

Jest 也通过了相同的测试(Jest 代码如下)。请注意,代码非常相似。我只是将

jest
替换为
vi

开玩笑代码

import { render, screen } from "@testing-library/react";
import ReactTestUtils from "react-dom/test-utils";
import { jest, it, expect } from "@jest/globals";
import React from "react";

it("window.onerror is called on unhandled error in event handler", async () => {
  const spy = jest.spyOn(console, "error");
  spy.mockImplementation(() => undefined);

  const caught = jest.fn();
  const App = () => {
    return (
      <button
        onClick={() => {
          throw new Error("ahhhh");
        }}
      >
        Error
      </button>
    );
  };

  class Tracker extends React.Component {
    componentDidMount() {
      window.onerror = (message, source, lineno, colno, error) => {
        caught();
        return true;
      };
    }

    render() {
      return this.props.children;
    }
  }

  render(
    <Tracker>
      <App />
    </Tracker>
  );

  expect(caught).toHaveBeenCalledTimes(0);
  const button = await screen.findByText("Error");

  try {
    // using ReactTestUtils here since it allows me to catch the error
    ReactTestUtils.Simulate.click(button);
  } catch (e) {
    // do nothing
  }

  expect(caught).toHaveBeenCalledTimes(1);
});

依赖关系

    "react": "^18.2.0",
    "react-dom": "^18.2.0"

    "@babel/preset-env": "^7.23.5",
    "@babel/preset-react": "^7.23.3",
    "@testing-library/react": "^14.1.2",
    "babel-jest": "^29.7.0",
    "jest": "^29.7.0",
    "jest-environment-jsdom": "^29.7.0",
    "react-test-renderer": "^18.2.0"

寻找解决此问题的方法。有什么办法可以在 vitest 中到达

window.onerror
吗?

reactjs react-testing-library jsdom vitest
1个回答
0
投票

在你的 vitest 设置中,你可以在测试之前手动模拟 window.onerror ,然后断言它是否被调用:

it("window.onerror is called on unhandled error in event handler", async () => {
  // ... other setup ...

  // Mock window.onerror
  const originalOnError = window.onerror;
  window.onerror = vi.fn();

  // ... rendering and event simulation ...

  // Check if window.onerror was called
  expect(window.onerror).toHaveBeenCalledTimes(1);

  // Restore original window.onerror
  window.onerror = originalOnError;
});

创建错误边界组件并用它来捕获错误:

class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false };
  }

  static getDerivedStateFromError(error) {
    return { hasError: true };
  }

  componentDidCatch(error, errorInfo) {
    // You can log the error here
    this.props.onError();
  }

  render() {
    if (this.state.hasError) {
      // Render fallback UI
      return <h1>Something went wrong.</h1>;
    }

    return this.props.children;
  }
}

it("error boundary catches error", async () => {
  const onError = vi.fn();

  render(
    <ErrorBoundary onError={onError}>
      <App />
    </ErrorBoundary>
  );

  // ... simulate the click ...

  expect(onError).toHaveBeenCalledTimes(1);
});

如果您可以控制错误抛出逻辑,则可以调度自定义事件并在测试中监听该事件:

// Inside your component
const handleClick = () => {
  try {
    throw new Error("ahhhh");
  } catch (e) {
    const event = new CustomEvent("customError", { detail: e });
    window.dispatchEvent(event);
  }
};

// In your test
window.addEventListener("customError", caught);

// ... simulate the click ...

expect(caught).toHaveBeenCalledTimes(1);
© www.soinside.com 2019 - 2024. All rights reserved.