如何使用Jest / Enzyme在React中编写ErrorBoundary的测试用例

时间:2018-04-07 13:24:34

标签: reactjs jestjs enzyme

我一直在尝试(没有成功)为通过componentDidCatch生命周期方法处理错误的ErrorBoundary组件编写测试用例。 尽管<ErrorBoundry>组件中的子组件产生了错误,但<ErrorBoundry>不会呈现有关代码中的错误的信息,而是错误组件的内容(如果它可以正常工作)。 组件在生产/开发中按预期工作,但在由Jest / Enzyme执行测试时不会。

测试错误:

 PASS  src/ErrorBoundary.test.js
  ● Console

    console.error node_modules/fbjs/lib/warning.js:33
      Warning: `value` prop on `input` should not be null. Consider using an empty string to clear the component or `undefined` for uncontrolled components.
          in input (at ErrorBoundary.test.js:11)
          in div (at ErrorBoundary.test.js:10)
          in ComponentWithError (at ErrorBoundary.test.js:26)
          in ErrorBoundry (created by WrapperComponent)
          in WrapperComponent
    console.log src/ErrorBoundary.test.js:29
      <ErrorBoundry>
        <ComponentWithError>
          <div>
            <input type="text" value={{...}} />
          </div>
        </ComponentWithError>
      </ErrorBoundry>

ErrorBoundry.js:

import React, { Component } from 'react'
import Raven from 'raven-js'
import { Segment, Button } from 'semantic-ui-react'

export default class ErrorBoundry extends Component {
    state = {
        hasError: false
    }

    componentDidCatch(error, info) {
        this.setState({ hasError: true })
        Raven.captureException(error, { extra: info });
    }

    render() {
        if(this.state.hasError) {
            return (
                <div className='error-boundry'>
                    <Segment>
                        <h2> Oh no! Somethin went wrong </h2>
                        <p>Our team has been notified, but click  
                            <Button  onClick={() => Raven.lastEventId() && Raven.showReportDialog()}> 
                            here </Button> to fill out a report.
                        </p>
                    </Segment>
                </div>
            );
        } else {
            return this.props.children;
        }
    }
}

ErrorBoundry.test.js:

import React, { Component } from 'react'
import ReactDOM from 'react-dom'
import renderer from 'react-test-renderer'
import { shallow, mount } from 'enzyme'
import ErrorBoundary from './ErrorBoundary'

class ComponentWithError extends Component {
  render() {
    return (
      <div>
        <input type = "text" value = {null}/>  
      </div>
    );
  }
}

describe('<ErrorBoundary> window',()=> {
  it('should match the snapshot', () => {
    const tree = renderer.create(<ErrorBoundary>Test</ErrorBoundary> ).toJSON()
    expect(tree).toMatchSnapshot()
  })

  it('displays error message on error generated by child', () => {
    const wrapper = mount(
      <ErrorBoundary > 
        <ComponentWithError />
      </ErrorBoundary> 
      )
    console.log(wrapper.debug() )
  })
})

3 个答案:

答案 0 :(得分:5)

经过进一步的研究后,我发现这是一个必须由Enzyme解决的开放性问题。 https://github.com/airbnb/enzyme/issues/1255

我已按如下方式实施:

function ProblemChild() {
  throw new Error('Error thrown from problem child');
  return <div>Error</div>; // eslint-disable-line
}

describe('<ErrorBoundary> window',()=> {  
  it('displays error message on error generated by child', () => {
    const spy = sinon.spy(ErrorBoundary.prototype, 'componentDidCatch')
    mount(<ErrorBoundary><ProblemChild /></ErrorBoundary>)
    chaiExpect(ErrorBoundary.prototype.componentDidCatch).to.have.property('callCount', 1)
  })
})

建议的解决方法无论如何都适用

  1. 仍然无法通过<ErrorBoundary>
  2. 测试呈现给应用用户的错误消息
  3. 测试控制台显示警告:

    PASS src/ErrorBoundary.test.js  ● Console

    console.error node_modules/react-dom/cjs/react-dom.development.js:9627
      The above error occurred in the <ProblemChild> component:
          in ProblemChild (at ErrorBoundary.test.js:37)
          in ErrorBoundry (created by WrapperComponent)
          in WrapperComponent
    
      React will try to recreate this component tree from scratch using the error boundary you provided, ErrorBoundry.
    

答案 1 :(得分:0)

hasError生命周期方法ComponentDidCatch状态发生更改以来,添加@AndreasKöberle的评论,您还可以使用酶setState

您也无需mount评论shallow would do

  it('displays error message on error generated by child', () => {
    const wrapper = shallow(
      <ErrorBoundary > 
        <ComponentWithError />
      </ErrorBoundary> 
    );
    wrapper.setState({ hasError: true });
    console.log(wrapper.debug() );    
  });

答案 2 :(得分:0)

酶现在有simulateError助手。

所以这对我来说很好:

const Something = () => null;

describe('ErrorBoundary', () => {
  it('should display an ErrorMessage if wrapped component throws', () => {
    const wrapper = mount(
      <ErrorBoundary>
        <Something />
      </ErrorBoundary>
    );

    const error = new Error('test');

    wrapper.find(Something).simulateError(error);

    /* The rest fo your test */
  }
}