如何在不失败测试的情况下获得 Jest toThrow 的覆盖
How to get coverage for Jest toThrow without failing test
假设我正在使用 jest --coverage
:
测试下面的 React 组件
class MyComponent extends React.Component {
constructor(props) {
super(props)
if (props.invalid) {
throw new Error('invalid')
}
}
}
覆盖率报告会说 throw new Error('invalid')
行未被覆盖。由于 .not.toThrow()
似乎没有涵盖任何内容,因此我使用 Enzyme 创建了以下测试:
const wrapper = shallow(
<MyComponent invalid />
)
it('should throw', () => {
function fn() {
if (wrapper.instance().props.invalid) {
throw new Error('invalid')
}
}
expect(fn).toThrow()
})
这条线被覆盖了!然而,测试本身失败并显示 encountered a declaration exception
- 这意味着原始组件抛出了错误(应该如此)?
我使用toThrow()
错了吗?
galki,我认为问题是您在 constructuring 组件时抛出错误。它没有通过测试,正如它应该的那样(你完全正确)。
相反,如果您可以在其他地方提取 prop-checking 函数,在安装过程中不会调用它 - 它会完美运行。例如,我将您的代码段修改为
export default class MyComponent extends React.Component {
constructor(props) {
super(props)
}
componentWillReceiveProps(nextProps) {
if (nextProps.invalid) {
throw new Error('invalid')
}
}
render() {
return (
<div/>
)
}
}
和
const wrapper = shallow(
<MyComponent />
)
it('should throw', () => {
function fn() {
wrapper.setProps({invalid: true});
};
expect(fn).toThrow();
})
因此,如果您有机会在安装时不抛出错误 - 您将能够对其进行测试。
显然这与 how React 16 handles errors 相关。我设法通过用具有 componentDidCatch
方法的父 React 组件包装 MyComponent
来使测试通过。
这使测试通过但影响覆盖率,我不得不将 shallow
更改为 mount
。测试结果如下所示:
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()
})
更新
在意识到问题是 shallow
或 mount
中抛出的错误(在进行测试之前)之后,我将整个事情简化为:
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)
})
意识到这是一个老问题,但对于未来的观众,我想我会扩展@galki 的回答。与其使用 try/catch,不如简单地将 shallow
/mount
包装在匿名函数中,然后使用 .toThrowError()
代替:
const TestComponent = () => {
throw new Error('Test error');
}
describe('Test Component', () => {
it('Throws an error', () => {
expect(() => shallow(<TestComponent />)).toThrowError();
});
});
这使您的代码更加简洁,结果相同。
假设我正在使用 jest --coverage
:
class MyComponent extends React.Component {
constructor(props) {
super(props)
if (props.invalid) {
throw new Error('invalid')
}
}
}
覆盖率报告会说 throw new Error('invalid')
行未被覆盖。由于 .not.toThrow()
似乎没有涵盖任何内容,因此我使用 Enzyme 创建了以下测试:
const wrapper = shallow(
<MyComponent invalid />
)
it('should throw', () => {
function fn() {
if (wrapper.instance().props.invalid) {
throw new Error('invalid')
}
}
expect(fn).toThrow()
})
这条线被覆盖了!然而,测试本身失败并显示 encountered a declaration exception
- 这意味着原始组件抛出了错误(应该如此)?
我使用toThrow()
错了吗?
galki,我认为问题是您在 constructuring 组件时抛出错误。它没有通过测试,正如它应该的那样(你完全正确)。 相反,如果您可以在其他地方提取 prop-checking 函数,在安装过程中不会调用它 - 它会完美运行。例如,我将您的代码段修改为
export default class MyComponent extends React.Component {
constructor(props) {
super(props)
}
componentWillReceiveProps(nextProps) {
if (nextProps.invalid) {
throw new Error('invalid')
}
}
render() {
return (
<div/>
)
}
}
和
const wrapper = shallow(
<MyComponent />
)
it('should throw', () => {
function fn() {
wrapper.setProps({invalid: true});
};
expect(fn).toThrow();
})
因此,如果您有机会在安装时不抛出错误 - 您将能够对其进行测试。
显然这与 how React 16 handles errors 相关。我设法通过用具有 componentDidCatch
方法的父 React 组件包装 MyComponent
来使测试通过。
这使测试通过但影响覆盖率,我不得不将 shallow
更改为 mount
。测试结果如下所示:
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()
})
更新
在意识到问题是 shallow
或 mount
中抛出的错误(在进行测试之前)之后,我将整个事情简化为:
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)
})
意识到这是一个老问题,但对于未来的观众,我想我会扩展@galki 的回答。与其使用 try/catch,不如简单地将 shallow
/mount
包装在匿名函数中,然后使用 .toThrowError()
代替:
const TestComponent = () => {
throw new Error('Test error');
}
describe('Test Component', () => {
it('Throws an error', () => {
expect(() => shallow(<TestComponent />)).toThrowError();
});
});
这使您的代码更加简洁,结果相同。