问题
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