[英]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.