在使用 Jest/Enzyme 进行测试期间检测 React 中的合成点击

Posted

技术标签:

【中文标题】在使用 Jest/Enzyme 进行测试期间检测 React 中的合成点击【英文标题】:Detect synthetic click in React during testing with Jest/Enzyme 【发布时间】:2017-07-24 20:35:13 【问题描述】:

我正在使用 React 构建一个应用程序。我在 react-bootstrap Button 的“后面”隐藏了一个文件输入元素 (<input type="file"/>),以便能够控制样式。因此,当单击按钮时,我转身并在文本输入元素上触发合成单击事件,如下所示。

class OpenFileButton extends React.Component 
  ...
  clickHandler() 
    this.refs['input'].click();
  

  render() 
    return (
      <ButtonGroup>
        <div>
          <input type="file" onChange=this.props.someCallback
            ref="input" style=display: 'none'/>
          <Button onClick=this.clickHandler>Open File</Button>
        </div>
      </ButtonGroup>
    );
  

我希望能够使用 Jest/Enzyme 进行测试。但是,虽然我可以模拟按钮上的点击事件,但我还没有弄清楚如何检测文件输入元素上的合成点击事件。

我曾尝试使用 Jest/Enzyme 来模拟输入元素上的点击方法。

const component = mount(<OpenFileButton/>);
const fileInput = component.find('input');
const button    = component.find('Button');
fileInput.click = jest.fn();
button.simulate('click');
expect(fileInput.click).toHaveBeenCalled();

但是,以这种方式模拟 click 方法是行不通的。我也无法添加onClick 属性,即fileInput.props().onClick = jest.fn() 不起作用。

This question 是关于在代码本身中检测合成点击事件,而不是在测试代码中,因此不相关。

那么,如何使用 Jest/Enzyme 检测 DOM 元素上的(合成)点击事件?

【问题讨论】:

【参考方案1】:

&lt;input /&gt;this.refs.inputhtmlInputElement 的一个实例。

然后你可以测试HTMLInputElement.prototype.click是否被调用。

使用sinon,您将拥有:

import sinon from 'sinon';
import mount from 'enzyme';

const clickInputSpy = sinon.spy(HTMLInputElement.prototype, 'click')
const component = mount(<OpenFileButton/>);

const button    = component.find('Button');

button.simulate('click');
expect(clickInputSpy.called).toBeTruthy();
clickInputSpy.restore();

const clickInputSpy = sinon.spy(HTMLInputElement.prototype, 'click');


console.log(
 'Is <input /> clicked ? ', clickInputSpy.called 
);
document.querySelector('input').click();


console.log(
 'Is <input /> clicked ? ', clickInputSpy.called 
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>


<script src="https://cdnjs.cloudflare.com/ajax/libs/sinon.js/1.15.4/sinon.min.js"></script>

<input />

【讨论】:

太棒了!谢谢你。完美运行。只是为了直接回答我的问题,为了对 Jasmine/Jest 而不是 Sinon 做同样的事情,请执行以下操作:(A)将您的 sinon.spy(... 替换为 spyOn(...。 (B) 将expect 语句更改为expect(clickInputSpy).toHaveBeenCalled()(或类似的东西)。 (C) 如果您需要在定义间谍的同一 itdescribe 块中恢复间谍,请按照建议的 here 使用 clickInputSpy.and.callThrough() 但是,有一个问题,虽然不一定完全无法克服。 any/all 输入元素的间谍间谍,而不是我感兴趣的特定元素。在许多情况下,可能不会有多个不同的 input 元素需要区分,所以可能没问题。但是,它确实使测试有些不理想。如果您想在相同类型的两个元素之间“转移”点击,它也会使该策略完全不可行,例如单击一个按钮,然后以编程方式单击另一个按钮。 所以我不知道是否可以直接窥探实例。如果是这样,请使用spy(component.ref('input'), 'click') 而不是spy(HTMLInputElement.prototype, 'click')。 ..它可能工作或spy(component.find('input').get(0), 'click') 感谢您的想法。我已经在我发布的替代答案中使用了您的最后一个建议,即spy(...find...get..., 'click'),它似乎正在工作。无论如何,再次感谢您最初的回答让我朝着正确的方向前进。【参考方案2】:

这里的解决方案涉及监视我感兴趣的特定文件输入元素的click 方法。因此,我可以检查是否在单击后调用了此 file-input-element-click-spy在button元素上模拟,如下:

const openFileButtonWrapper = mount(<OpenFileButton/>);
const buttonWrapper = openFileButtonWrapper.find(Button);
const fileInputWrapper = openFileButtonWrapper.find('input [type="file"]');
const fileInput = fileInputWrapper.get(0);
const clickInputSpy = spyOn(fileInput, 'click');
buttonWrapper.simulate('click');
expect(clickInputSpy).toHaveBeenCalled();

@AbdennourTOUMI 的回答使用了 Sinon 的 spy 方法,这提醒我 Jest 使用了一些 Jasmine 功能,包括它的 spyOn 方法,这在 Jest 文档中并不明显。因此,即使其他答案最终监视了 _all_ 输入元素,这并不理想,但它确实让我朝着正确的方向前进,所以谢谢你,Adbennour。

【讨论】:

以上是关于在使用 Jest/Enzyme 进行测试期间检测 React 中的合成点击的主要内容,如果未能解决你的问题,请参考以下文章

使用 React Suspense 和 React.lazy 子组件进行 Jest/Enzyme 类组件测试

如何为 createSlice() 的 extraReducers 编写测试(Jest + Enzyme)?

Jest / Enzyme - 生命周期方法中的模拟异步函数

使用 Jest / Enzyme 在 React 中的功能组件内部测试方法

React / Enzyme:运行 Jest / Enzyme 测试时出现 Invariant Violation 错误

如何使用 jest/enzyme 测试 useEffect 与 useDispatch 挂钩?