[英]How to access internal pieces of react component and mock api call with jest & enzyme testing framework?
我正在为一个带有jest和酶的react-redux组件制作一个javascript单元测试套件; 有问题的组件是一个文件上载表单,其中包含一些字段,这些字段根据用户输入和/或服务器响应进行了一些基本样式更改。 Jest和酶都已设置好并且正常工作,我能够成功地编写基本测试。
我想使用jest和酶来模拟用户与这些UI元素(文件输入和文本字段)的交互,但我很难这样做; 我对使用这些测试框架并且对react-redux有一个基本的了解是相当新的,我有点不知所措,以确保正确的部分到位,以便编写有意义的单元测试。
一般情况下:这个单元测试是在事实之后进行的,几个月前我加入了这个使用测试驱动开发技术的项目,我之前没有开发过,所以我将它作为未来开发的示例/模板。
截至目前,单元测试成功地创建了组件(使用酶的mount
和shallow
的mockStore和初始状态)。 我试过做两件事:
我在这两种情况下都没有完全成功,并且有单元测试,涵盖了这些方面,但没有完全集成的方式。
这是我试图进行单元测试的组件的render方法:
render() {
const { dataset } = this.props;
let serverResp;
dataset ? serverResp = dataset.fileUploadResp : null;
let catFeats = this.state.catFeatures;
let errorMsg = this.state.errorResp;
let ordFeatureSelection = "";
let catFeatureSelection = "";
let dataPrevTable = this.getDataTablePreview();
// default to hidden until a file is selected, then display input areas
let formInputClass = "file-upload-form-hide-inputs";
// server message to display in popup (or other UI element)
serverResp ? serverResp = ( <p style={{display: 'block'}}> {JSON.stringify(serverResp)} </p> ) :
null;
// check if file with filename has been selected, if so then use css to show form
this.state.selectedFile && this.state.selectedFile.name ?
formInputClass = "file-upload-form-show-inputs" : null;
return (
<div>
<Form inverted>
<Segment className="file-upload-segment">
<Input
className="file-upload-form-text-input"
type="file"
label="Select new dataset"
id="upload_dataset_file_browser_button"
onChange={this.handleSelectedFile}
/>
<br/>
<div
id="file-upload-form-input-area"
className={formInputClass}
>
<Form.Input
label="Dependent Column"
placeholder="class"
value={this.state.dependentCol ? this.state.dependentCol : ""}
type="text"
onChange={this.handleDepColField}
/>
<Form.Input
label="Ordinal Features"
>
<textarea
label="Ordinal Features"
placeholder={"{\"ord_feat_1\": [\"MALE\", \"FEMALE\"], \"ord_feat_2\": [\"FIRST\", \"SECOND\", \"THIRD\"]}"}
onChange={this.handleOrdinalFeatures}
/>
</Form.Input>
<Form.Input
label="Categorical Features"
>
<textarea
label="Categorical Features"
placeholder={"cat_feat_1, cat_feat_2"}
onChange={this.handleCatFeatures}
/>
</Form.Input>
<Popup
header="Error Submitting Dataset"
content={serverResp}
open={errorMsg ? true : false}
trigger={
<Button
inverted
color="blue"
compact
size="small"
icon="upload"
content="Upload dataset"
onClick={this.handleUpload}
/>
}
/>
</div>
</Segment>
</Form>
{dataPrevTable}
</div>
);
}
它是一个相当基本的反应组件,连接到app redux商店,带有一些语义ui的东西,可以很好地造型。 有一个带有文件输入,文本区域输入和按钮/弹出窗口的表单; 如果未选择任何文件,则仅显示文件输入,如果提交文件的尝试不成功,则弹出窗口中将显示错误消息。
对于第1点 - 我试图遵循jest中的指南/ docs并创建组件使用的模拟api片段并使用jest.mock()
jest.mock('../../utils/apiHelper');
import uploadDataset from '../../data/datasets/dataset/api';
但是当我尝试时,在单元测试中
it('testing promise for successfully case, proper dependent_col', () => {
expect.assertions(1);
return uploadDataset(fakeDataset).then(data => expect(data.name).toEqual('iris.csv'));
})
测试因TypeError: (0 , _api2.default) is not a function
失败TypeError: (0 , _api2.default) is not a function
我直接导入mock api并直接调用它TypeError: (0 , _api2.default) is not a function
,它工作正常但无法测试应该进行api调用的模块; 我觉得以这种方式测试这个功能几乎是一个没有实际意义,并且更愿意以正确模拟它实际工作方式的方式测试组件,即触发导致api调用的事件而不是直接调用api 。 应该如何使用jest.mock()
? 我不正确地使用它?
对于第2点 - 我正在直接操作组件反应状态并检查UI更新,而不是通过表单字段模拟用户输入。 我尝试使用酶的find来访问文件输入字段
let testFileUpload;
beforeEach(() => {
testFileUpload = mount(<FileUpload store={store} testProp="hello" />);
})
... other tests ...
it('click file upload button', () => {
testFileUpload.find('#upload_dataset_file_browser_button').simulate("change",
{
target: {
files: [ 'iris.csv' ]
}
});
})
}
并且测试失败并出现错误Method “simulate” is meant to be run on 1 node. 2 found instead.
Method “simulate” is meant to be run on 1 node. 2 found instead.
我应该如何通过ID访问文件输入/ html元素和/或酶对模拟的期望是什么? 我花了最近几天查看jest / enzyme文档和示例,需要一些帮助。 当只有一个具有该id的元素时,为什么find方法返回2个节点? 我应该如何在所述节点上模拟文件选择的更改事件? 与上面提到的解决方法类似,我正在直接更改组件的状态并从该点检查事物,但我更愿意尽可能地模拟实际用例。
任何帮助是极大的赞赏。
尽管如此,是否可以绕过单元测试中的某些事情来基本上完成相同的结果? 有了我的问题,我在模拟输入到表单字段时遇到了一些麻烦,该表单字段应该在用户输入/更改时使用react setState,所以我只是直接更改状态而不是通过UI元素获取输入。 熟悉单元测试的人认为,这种单元测试方式是否足够或者这是非常不正确的? 现在我几乎是唯一一个为这个项目的前端UI组件进行单元测试的人,我不太确定如何正确测试这些部分。
不确定为什么带有#id的元素被挂载两次。 但你可以使用索引:
testFileUpload.find('#upload_dataset_file_browser_button').at(0).simulate("change")
我曾经认为酶的工作原理与jquery与css选择器相同。 但似乎不是。 即使是库的创建者也不建议使用带模拟操作的mount。 但我仍然使用,因为这是我在测试中获得良好报道的最佳方式。 使用酶我主要使用元素选择器而不是css选择器。
我使用prop
来模拟动作,因为模拟不能真正“模拟”自定义动作。 尝试这个:
testFileUpload.find(Input).prop('onChange')('paramatertosendtofunction')
编辑:您可以使用辅助函数
function flushPromises() {
return new Promise(resolve => setImmediate(resolve));
}
test('', () => {
testFileUpload.find(Input).prop('onChange')('paramatertosendtofunction')
testFileUpload.update()
return flushPromises().then(() => {
expect(textFileUpload.state.foo).toBe('bar')
});
})
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.