【问题标题】:Jest/Enzyme Shallow testing RFC - not firing jest.fn()Jest/Enzyme 浅层测试 RFC - 不触发 jest.fn()
【发布时间】:2019-10-11 16:38:06
【问题描述】:

我正在尝试测试 RFC 上输入的 onChange 属性(和值)。在测试中,尝试模拟事件不会触发 jest mock 函数。

实际组件已连接(使用 redux),但我也将它作为未连接组件导出,因此我可以进行浅层单元测试。我还在使用一些 react-spring 钩子来制作动画。

我也尝试挂载而不是浅化组件,但我仍然遇到同样的问题。


我的组件



export const UnconnectedSearchInput: React.FC<INT.IInputProps> = ({ scrolled, getUserInputRequest }): JSX.Element => {

  const [change, setChange] = useState<string>('')


  const handleChange = (e: InputVal): void => {
    setChange(e.target.value)
  }

  const handleKeyUp = (): void => {
    getUserInputRequest(change)
  }

  return (
    <animated.div
      className="search-input"
      data-test="component-search-input"
      style={animateInputContainer}>

      <animated.input
        type="text"
        name="search"
        className="search-input__inp"
        data-test="search-input"
        style={animateInput}
        onChange={handleChange}
        onKeyUp={handleKeyUp}
        value={change}
      />
    </animated.div>
  )
}


export default connect(null, { getUserInputRequest })(UnconnectedSearchInput); 



我的测试

在这里您可以看到失败的测试。注释掉的代码是我迄今为止尝试过的其他事情,但没有任何运气。



describe('test input and dispatch action', () => {
  let changeValueMock
  let wrapper
  const userInput = 'matrix'

  beforeEach(() => {
    changeValueMock = jest.fn()
    const props = {
      handleChange: changeValueMock
    }

    wrapper = shallow(<UnconnectedSearchInput {...props} />).dive()
    // wrapper = mount(<UnconnectedSearchInput {...props} />)
  })

  test('should update input value', () => {
    const input = findByTestAttr(wrapper, 'search-input').dive()
    // const component = findByTestAttr(wrapper, 'search-input').last()

    expect(input.name()).toBe('input')
    expect(changeValueMock).not.toHaveBeenCalled()

    input.props().onChange({ target: { value: userInput } }) // not geting called
    // input.simulate('change', { target: { value: userInput } })

    // used with mount
    // act(() => {
    //   input.props().onChange({ target: { value: userInput } })
    // })
    // wrapper.update()

    expect(changeValueMock).toBeCalledTimes(1)

    // expect(input.prop('value')).toBe(userInput);
  })
})


测试错误

这里没有什么特别的。

 expect(jest.fn()).toBeCalledTimes(1)

    Expected mock function to have been called one time, but it was called zero times.

      71 |     // wrapper.update()
      72 | 
    > 73 |     expect(changeValueMock).toBeCalledTimes(1)

任何帮助将不胜感激,因为它已经 2 天了,我不知道这一点。

【问题讨论】:

  • getUserInputRequesthandleKeyUp 内部被调用,当你执行input.props().onChange({ target: { value: userInput } }) 时它会调用handleChange,而不是handleKeyUp
  • 是的,没错。变量的命名约定错误。仍然不影响结果,因为我实际上想测试handleChange。将更新以避免任何混淆。谢谢
  • 在您的测试中,您将属性handleChange 传递给组件UnconnectedSearchInput。注意UnconnectedSearchInput不使用属性handleChange,所以这个属性永远不会被调用是正常的
  • 等等,我很困惑。我将模拟作为handleChange 传递给UnconnectedSearchInput,它在实际组件的onChange={handleChange} 上的input 元素上被调用。我在这里错过了什么?
  • 当您编写 onChange={handleChange} 时,它会检索您在 const handleChange = (e: InputVal): void =&gt; { setChange(e.target.value) } 之前声明的 handleChange 函数,而不是来自 props 的函数

标签: reactjs jestjs enzyme


【解决方案1】:

您不必与组件内部交互;而是更好地使用公共接口:道具和渲染结果

test('should update input value', () => {
  expect(findByTestAttr(wrapper, 'search-input').dive().props().value).toEqual('');
  findByTestAttr(wrapper, 'search-input').dive().props().onChange({ target: {value: '_test_'} });
  expect(findByTestAttr(wrapper, 'search-input').dive().props().value).toEqual('_test_');
}

您不需要检查是否调用了某个内部方法,它的名称或参数是什么。如果你得到了你需要的东西——你需要&lt;input&gt;和一些预期的value——它是怎么发生的都没关系。

但是如果函数是从外部传递的(通过props),你肯定想验证它是否在某些预期的情况下被调用

test('should call getUserInputRequest prop on keyUp event', () => {
  const getUserInputRequest = jest.fn();
  const mockedEvent = { target: { key: 'A' } };
  const = wrapper = shallow(<UnconnectedSearchInput getUserInputRequest={getUserInputRequest } />).dive()
  findByTestAttr(wrapper, 'search-input').dive().props().onKeyUp(mockedEvent)
  expect(getUserInputRequest).toHaveBeenCalledTimes(1);
  expect(getUserInputRequest).toHaveBeenCalledWith(mockedEvent);
}

[UPD] 似乎像在中间变量中缓存选择器一样

const input = findByTestAttr(wrapper, 'search-input').dive();
input.props().onChange({ target: {value: '_test_'} });
expect(input.props().value).toEqual('_test_');

没有通过,因为input 指的是value 不更新的陈旧对象。

在酶的 github 上,我 been answered 这是预期的行为:

这是酶 v3 中的预期行为 - 请参阅 https://github.com/airbnb/enzyme/blob/master/docs/guides/migration-from-2-to-3.md#calling-props-after-a-state-change

所以是的,完全正确 - 如果有任何变化,必须从根目录重新找到所有内容。

【讨论】:

  • 感谢@skyboyer 的示例。您对should update input value 的第一个案例实际上是我尝试的第一件事,但没有成功(只是再次检查以防我之前错过了什么)。事实上,最后一个断言我得到一个空字符串Expected: "test" Received: ""
  • 抱歉,没有使用 react-test-renderer 的经验。尝试用expect(findByTestAttr(wrapper, 'search-input').dive().props().value).toEqual('');替换expect(input.props().value).toEqual('');
  • 或者在...props().change(...)之后你可以尝试添加await Promise.resolve()以防react-test-renderer以异步方式更新元素
  • 但在最后一个断言上,我想我实际上应该期望一个字符串而不是一个空字符串,对吧? :) 无论如何,作为一个随机想法,我正在考虑将组件拆分为容器和展示,这样我就可以将 handleChange 也作为道具传递
  • 对不起,不要听懂你的意思。另外我决定你使用react-test-renderer,因为findByTestAttr,但标签说它是酶。那你用什么库?
猜你喜欢
  • 2019-01-28
  • 2020-03-30
  • 2018-05-20
  • 2018-09-14
  • 2018-12-16
  • 2020-03-12
  • 2023-03-06
  • 2019-01-29
  • 1970-01-01
相关资源
最近更新 更多