在使用 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】:<input />
或this.refs.input
是htmlInputElement
的一个实例。
然后你可以测试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) 如果您需要在定义间谍的同一 it
或 describe
块中恢复间谍,请按照建议的 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 错误