![](/img/trans.png)
[英]How do I convert this chained promises code with callback to async/await
[英]How do I test async fetch in react componentDidMount with chained promises?
我一直在嘗試了解如何測試在componentDidMount
期間運行異步提取的已安裝componentDidMount
。
問題是我可以讓它等待初始提取觸發,但不要等待從promise中解析所有鏈。
這是一個例子:
import React from "react";
class App extends React.Component {
state = {
groceries: [],
errorStatus: ""
};
componentDidMount() {
console.log("calling fetch");
fetch("/api/v1/groceries")
.then(this.checkStatus)
.then(this.parseJSON)
.then(this.setStateFromData)
.catch(this.setError);
}
checkStatus = results => {
if (results.status >= 400) {
console.log("bad status");
throw new Error("Bad Status");
}
return results;
};
setError = () => {
console.log("error thrown");
return this.setState({ errorStatus: "Error fetching groceries" });
};
parseJSON = results => {
console.log("parse json");
return results.json();
};
setStateFromData = data => {
console.log("setting state");
return this.setState({ groceries: data.groceries });
};
render() {
const { groceries } = this.state;
return (
<div id="app">
{groceries.map(grocery => {
return <div key={grocery.id}>{grocery.item}</div>;
})}
</div>
);
}
}
export default App;
測試:
import Enzyme from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
import React from 'react';
import { mount } from 'enzyme'
import App from './App';
Enzyme.configure({ adapter: new Adapter() });
const mockResponse = (status, statusText, response) => {
return new window.Response(response, {
status: status,
statusText: statusText,
headers: {
'Content-type': 'application/json'
}
});
};
describe('App', () => {
describe('componentDidMount', () => {
it('sets the state componentDidMount', async () => {
console.log('starting test for 200')
global.fetch = jest.fn().mockImplementation(() => Promise.resolve(
mockResponse(
200,
null,
JSON.stringify({
groceries: [
{ item: 'nuts', id: 10 }, { item: 'greens', id: 3 }
]
})
)
));
const renderedComponent = await mount(<App />)
await renderedComponent.update()
console.log('finished test for 200')
expect(renderedComponent.state('groceries').length).toEqual(2)
})
it('sets the state componentDidMount on error', async () => {
console.log('starting test for 500')
window.fetch = jest.fn().mockImplementation(() => Promise.resolve(
mockResponse(
400,
'Test Error',
JSON.stringify({ status: 400, statusText: 'Test Error!' })
)
))
const renderedComponent = await mount(<App />)
await renderedComponent.update()
console.log('finished test for 500')
expect(renderedComponent.state('errorStatus')).toEqual('Error fetching groceries')
})
})
})
當它運行時,我收到此控制台日志記錄的順序(請注意,測試完成,然后記錄該狀態已設置):
console.log src/App.test.js:22
starting test for 200
console.log src/App.js:10
calling fetch
console.log src/App.js:36
parse json
console.log src/App.test.js:39
finished test for 200
console.log src/App.js:42
setting state
我已經創建了一個代碼的示例沙箱:
這在我的應用程序中被抽象得更多,因此更改代碼本身要困難得多(例如,我想在更高的組件上進行測試,該組件具有redux存儲,並且這個較低的組件調用fetch,並最終通過設置存儲一個thunk)。
這是如何測試的?
更新方法實際上沒有返回承諾,這就是await無法正常工作的原因。 要修復單元測試,您可以將fetch調用移動到另一個方法,並在測試中使用該函數,以便等待正常工作。
componentDidMount() {
console.log("calling fetch");
this.fetchCall();
}
fetchCall() {
return fetch("/api/v1/groceries")
.then(this.checkStatus)
.then(this.parseJSON)
.then(this.setStateFromData)
.catch(this.setError);
}
使用instance()來訪問fetchCall方法。
const renderedComponent = mount(<App />);
await renderedComponent.instance().fetchCall();
我修改了代碼框中的上述更改: https ://codesandbox.io/s/k38m6y89o7
我不知道為什么await renderedComponent.update()
.update
await renderedComponent.update()
在這里沒有幫助( .update
不返回Promise但它仍然意味着下面的所有內容都是分開的微任務)。
但將東西包裝到setTimeout(..., 0)
對我setTimeout(..., 0)
。 所以微任務和macrotask之間的區別實際上是以某種方式發生的。
it("sets the state componentDidMount on error", done => {
console.log("starting test for 500");
window.fetch = jest
.fn()
.mockImplementation(() =>
Promise.resolve(
mockResponse(
400,
"Test Error",
JSON.stringify({ status: 400, statusText: "Test Error!" })
)
)
);
const renderedComponent = mount(<App />);
setTimeout(() => {
renderedComponent.update();
console.log("finished test for 500");
expect(renderedComponent.state("errorStatus")).toEqual(
"Error fetching groceries"
);
done();
}, 0);
});
});
這種方法的唯一缺點是:當expect()
失敗時,它不會向Jest輸出顯示失敗的消息。 Jest只是抱怨測試還沒有在5000毫秒完成。 在同一時間有效的錯誤消息,如Expected value to equal: ...
轉到控制台。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.