测试依赖于其他副作用的React Hooks副作用(或其他测试)

时间:2019-11-24 04:01:58

标签: reactjs testing jestjs react-hooks enzyme

我有一个具有简单输入和按钮的React Hook,如果没有输入,该按钮将被禁用,并且启用该按钮后将执行提取操作:

function MyComponent() {
  const [ value, setValue ] = useState('')

  function apiRequest() {
    if (!value) {
      return
    }

    axios.get('url')
      .then(console.log)
      .catch(console.log)
  }

  return (
    <div>
      <input onChange={e => setValue(e.target.value)} value={value} />
      <button disabled={!value} onClick={apiRequest}>
        Submit
      </button>
    </div>
  )
}

我用Enzyme编写了两个测试。第一个测试被禁用的道具是否正确,第二个测试它是否实际获取。

it('sets the disabled prop appropriately', function() {
    const wrapper = mount(<MyComponent />)
    const input = wrapper.find('input')
    const btn = wrapper.find('button')
    expect(btn.prop('disabled')).toBeTruthy()
    input.simulate('change', 'abc123')
    expect(btn.prop('disabled')).toBeFalsy()
})

it('fetches on submit', function () {
    const wrapper = mount(<MyComponent />)
    const input = wrapper.find('input')
    const btn = wrapper.find('button')
    input.simulate('change', 'abc123')
    btn.simulate('click')
    expect(axios.get).toHaveBeenCalled()
})

但是不幸的是,要使第二项测试生效,需要启用该按钮,因此必须首先输入文本。因此,实际上,第二项测试也无意中也测试了禁用的道具,因为如果禁用的道具设置不正确,它将失败(onClick不会触发)。

我遵循了React的recommended approach

test React components without relying on their implementation details

这是react-testing-library的核心原则,所以我纯粹是在测试副作用。我使用的是酶而不是酶,因为我的团队目前正在使用酶

我如何能够重写第二个测试,以便只能 测试提取?预先感谢。

编辑:或者,有几种方法可以重写它以正确测试提取?

2 个答案:

答案 0 :(得分:1)

您可以做的一件事是将<div>替换为<form>,然后向其中添加onSubmit={e => apiRequest(value)},以便按钮可以保持禁用状态,并且您仍可以继续进行测试而无需介绍不必要的外部因素。

此外,将function apiRequest() {...}移出组件。它可以使用value作为参数,而不是依赖周围的范围。

// You could even export this separately and make a test exclusively for this
// without also having to test the form itself
function apiRequest ( value ) {
    if (!value) {
      return
    }

    axios.get('url')
      .then(console.log)
      .catch(console.log)
}

function MyComponent() {
  const [ value, setValue ] = useState('')

  return (
    <form onSubmit={e => { e.preventDefault(); apiRequest(value); }}>
        <input onChange={e => setValue(e.target.value)} value={value} />
        <button disabled={!value}>
            Submit
        </button>
    </form>
  )
}

答案 1 :(得分:0)

我相信您肯定是在测试行为,而不是实现细节。

如果您说的是setState(),那将依赖于实现细节(请确保它不适用于功能组件,这就是为什么它是错误的模式)。

使用RTL,您仍然必须在单击按钮之前更改输入,这里没有区别。

唯一的事情是实现细节,它依赖于axios本身。您可以使用nock处理任何库发出的任何请求。但是我不确定这是否值得。