当抛出事件处理程序时,我无法在使用 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
吗?
在你的 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);