如何在不失败测试的情况下获得 Jest toThrow 的覆盖率

Posted

技术标签:

【中文标题】如何在不失败测试的情况下获得 Jest toThrow 的覆盖率【英文标题】:How to get coverage for Jest toThrow without failing test 【发布时间】:2018-03-22 04:00:04 【问题描述】:

假设我正在使用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() 似乎没有涵盖任何内容,因此我使用酶创建了以下测试:

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() 错了吗?

【问题讨论】:

不应该是expect(fn()).toThrow() @AndreasKöberle no 【参考方案1】:

意识到这是一个老问题,但对于未来的观众,我想我会扩展 @galki 的答案。您可以简单地将 shallow/mount 包装在匿名函数中,然后使用 .toThrowError(),而不是使用 try/catch:

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


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

这为您提供更简洁的代码,但结果相同。

【讨论】:

这也适用于无状态:expect(() =&gt; shallow(TestComponent())).toThrowError();【参考方案2】:

显然这与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()
)

更新

在意识到问题是在shallowmount 中抛出错误后(在测试之前),我将整个事情简化为:

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)
)

【讨论】:

哇,你救了我,这是正确的。 .toThrow()仅适用于纯函数,如果我们使用渲染器或浅层,则此测试不起作用。 不必使用let 并分配try/catch 的结果,为什么不将shallow() 包装在一个匿名函数中并在其上调用.toThrowError()?例如。 expect(() =&gt; shallow(&lt;TestComponent /&gt;)).toThrowError();【参考方案3】:

galki,我认为问题在于您在 constructuring 组件时抛出错误。它没有通过测试,因为它应该(你完全正确)。 相反,如果您可以在其他地方提取 prop-checking 功能,在安装期间不会调用它 - 它会完美运行。比如我把你的sn-ps修改为

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();
)

因此,如果您有机会在安装时不抛出错误 - 您将能够对其进行测试。

【讨论】:

componentWillReceiveProps 在这种情况下不会被调用。我喜欢测试组件创建,而不是更新

以上是关于如何在不失败测试的情况下获得 Jest toThrow 的覆盖率的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 Jest 测试部分对象?

用 jest 测试 Vue 过滤器

如何在不导致脚本失败的情况下满足 shellcheck?

Nightwatchjs:如何在不创建错误/失败/异常的情况下检查元素是不是存在

如何在没有 UI 的情况下对钩子进行 Jest 测试

如何在不关闭和重新加载应用程序的情况下获得新的 JSON 响应