How to get coverage for Jest toThrow without failing test

試著忘記壹切 提交于 2020-01-05 05:09:09

问题


Let's say I'm testing the below React component with jest --coverage:

class MyComponent extends React.Component {
  constructor(props) {
    super(props)
    if (props.invalid) {
      throw new Error('invalid')
    }
  }
}

the coverage report will say that the line throw new Error('invalid') is uncovered. Since .not.toThrow() doesn't seem to cover anything I create the below test with Enzyme:

const wrapper = shallow(
  <MyComponent invalid />
)

it('should throw', () => {
  function fn() {
    if (wrapper.instance().props.invalid) {
      throw new Error('invalid')
    }
  }
  expect(fn).toThrow()
})

and the line gets covered! However the test itself fails with encountered a declaration exception - meaning the original component threw the error (as it should)?

Am I using toThrow() wrong?


回答1:


Realise this is an old question, but for future viewers, I thought I'd expand on @galki's answer. Rather than using a try/catch, you could simply wrap your shallow/mount in an anonymous function and then use .toThrowError() instead:

const TestComponent = () => {
    throw new Error('Test error');
}

describe('Test Component', () => {
    it('Throws an error', () => {
        expect(() => shallow(<TestComponent />)).toThrowError();
    });
});

This gives you much cleaner code, with the same result.




回答2:


Apparently this is connected to how React 16 handles errors. I managed to get the test passing by wrapping MyComponent with a parent React component that has a componentDidCatch method.

This made the test pass but to affect the coverage, I had to change shallow to mount. The test ended up looking like this:

class ParentComponent extends React.Component {
  componentDidCatch(error) {
    // console.log(error)
  }
  render() {
    return this.props.children
  }
}

class MyComponent extends React.Component {
  constructor(props) {
    super(props)
    if (props.invalid) {
      throw new Error('invalid')
    }
  }
}

const wrapper = mount(
  <ParentComponent>
    <MyComponent invalid />
  </ParentComponent>
)

it('should throw', () => {
  function fn() {
    if (wrapper.props().invalid) {
      throw new Error('invalid test')
    }
  }
  expect(fn).toThrow()
})

UPDATE

After realizing that the problem was an error being thrown inside shallow or mount (before it got to the test), I simplified the whole thing to this:

class MyComponent extends React.Component {
  constructor(props) {
    super(props)
    if (props.invalid) {
      throw new Error('invalid')
    }
  }
}

it('should throw', () => {
  let error
  try {
    shallow(<MyComponent invalid />)
  } catch (e) {
    error = e
  }
  expect(error).toBeInstanceOf(Error)
})



回答3:


galki, I think the problem is that you throw error while constructuring component. And it fails test, as it should (you're totally right). Instead, if you can extract prop-checking function somewhere else, where it will not be called during mounting - it will work perfectly. For example, I modified your snippets as

export default class MyComponent extends React.Component {
  constructor(props) {
    super(props)
  }
  componentWillReceiveProps(nextProps) {
    if (nextProps.invalid) {
      throw new Error('invalid')
    }
  }
  render() {
    return (
      <div/>
    )
  }
}

and

const wrapper = shallow(
 <MyComponent />
)

it('should throw', () => {
  function fn() {
    wrapper.setProps({invalid: true});
  };
  expect(fn).toThrow();
})

So, if you have a chance to not throw an error while mounting - you will be able to test it.



来源:https://stackoverflow.com/questions/49420305/how-to-get-coverage-for-jest-tothrow-without-failing-test

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!