【问题标题】:Jest test passes but error not caught and logs to consoleJest 测试通过但未捕获错误并记录到控制台
【发布时间】:2018-02-14 11:35:40
【问题描述】:

有没有办法在 Jest 中测试组件中的自定义错误,而不会在控制台中抛出 Uncaught 错误?

这里我有一个简单的按钮组件:

import React from 'react';

export default class Button extends React.Component {
    render() {

        if (!this.props.type) {
            throw new Error('Button requires a type prop');
        }

        return (
            <button className={`btn btn-${this.props.type}`}>Button</button>
        );
    }
}

在不提供 type 属性的情况下使用组件时,我希望抛出我的自定义错误。我还有以下 Jest 测试:

import React from 'react';
import ReactDOM from 'react-dom';
import Button from './button';

it('throws an error if the type prop is not defined', () => {
    const buttonType = undefined;
    const container = document.createElement('div');

    expect(() => {
        ReactDOM.render(<Button type={buttonType} />, container);
    }).toThrow('Button requires a type prop');
});

单元测试通过,但是控制台产生类似于以下的错误:

console.error node_modules/jsdom/lib/jsdom/virtual-console.js:29
Error: Uncaught [Error: Button requires a type prop]

The above error occurred in the <Button> component:
in Button (at button.spec.js:20)

Consider adding an error boundary to your tree to customize error handling behavior.
Visit https://reactjs.org/docs/error-boundaries.html to learn more about error boundaries.

通常在 Jasmine 中,.toThrow() 匹配器会自动捕获错误并且不会有日志记录。

我已经阅读了有关错误边界的信息,但这些似乎是在应用程序级别,而不是在组件级别。

我是否缺少更适合测试的方法?

编辑:使用以下版本:

  • 反应:16.2.0
  • 反应域:16.2.0
  • 开玩笑:22.2.2

【问题讨论】:

  • 任何解决方案对您有用吗?我都试过了。没有成功。

标签: reactjs jestjs


【解决方案1】:

我在 jsdom 的虚拟控制台中遇到了类似的问题,打印错误而不是抛出错误。据我所知,Jest 或任何其他测试框架无法阻止代码打印到控制台。

我已通过替换负责打印到控制台的侦听器来解决此问题。以下代码运行在 Jest 配置中setupFiles 中配置的文件中。

// There should be a single listener which simply prints to the 
// console. We will wrap that listener in our own listener.
const listeners = window._virtualConsole.listeners("jsdomError");
const originalListener = listeners && listeners[0];

window._virtualConsole.removeAllListeners("jsdomError");

window._virtualConsole.addListener("jsdomError", (error) => {
  if (error.type !== "not implemented" && originalListener) {
    originalListener(error);
  }
  // swallow
});

如您所见,在我们的例子中,打印到控制台的错误(多次,因为它是副作用,而不是我们测试的主要目的)是“未实现”错误。所以这段代码只会吞下那些类型的错误。

这个解决方案并不理想,但它会使日志保持清洁。

PS:如果您想降低吞下“真实”错误的风险,您可以在测试套件的before 方法中使用此技巧,并使用after 方法重置原始行为。

【讨论】:

    【解决方案2】:

    问题确实是 React 的错误处理防止错误冒泡到 Jasmine 可以检测到的顶层。两种可能的解决方法:

    Monkeypatch console.error 实际抛出

    console.error = msg => { throw new Error(msg); };
    

    这显然是 hack,如果 React 更改为使用不同的错误报告方法,它就会崩溃。 stijndepestel 的回答是对此的一种更稳健的方法。

    为测试创建错误边界

    let errorInfo = null;
    class ErrorBoundary extends React.PureComponent {
        state = {hasError: false};
    
        componentDidCatch(err, info) {
            errorInfo = [err, info]
            this.setState({hasError: true});
        }
    
        render() {
            if (!this.state.hasError)
                return React.Children.only(this.props.children);
            return 'Errored';
        }
    }
    

    然后您可以将您的测试包装在像这样的处理程序中,然后断言errorInfo 不为空,并且它包含您所期望的错误

    【讨论】:

      猜你喜欢
      • 2019-03-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-12-21
      • 2019-08-16
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多