Javascript 如何测试componentDidMount中设置React组件状态的异步调用
测试Javascript 如何测试componentDidMount中设置React组件状态的异步调用,javascript,reactjs,mocha.js,chai,enzyme,Javascript,Reactjs,Mocha.js,Chai,Enzyme,测试componentDidMount中的异步调用设置React组件状态的最佳方法是什么?对于上下文,我用于测试的库有Mocha、Chai、Enzyme和Sinon 下面是一个示例代码: /* * assume a record looks like this: * { id: number, name: string, utility: number } */ // asyncComponent.js class AsyncComponent extends React.Compon
componentDidMount
中的异步调用设置React组件状态的最佳方法是什么?对于上下文,我用于测试的库有Mocha
、Chai
、Enzyme
和Sinon
下面是一个示例代码:
/*
* assume a record looks like this:
* { id: number, name: string, utility: number }
*/
// asyncComponent.js
class AsyncComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
records: []
};
}
componentDidMount() {
// assume that I'm using a library like `superagent` to make ajax calls that returns Promises
request.get('/some/url/that/returns/my/data').then((data) => {
this.setState({
records: data.records
});
});
}
render() {
return (
<div className="async_component">
{ this._renderList() }
</div>
);
}
_renderList() {
return this.state.records.map((record) => {
return (
<div className="record">
<p>{ record.name }</p>
<p>{ record.utility }</p>
</div>
);
});
}
}
// asyncComponentTests.js
describe("Async Component Tests", () => {
it("should render correctly after setState in componentDidMount executes", () => {
// I'm thinking of using a library like `nock` to mock the http request
nock("http://some.url.com")
.get("/some/url/that/returns/my/data")
.reply(200, {
data: [
{ id: 1, name: "willson", utility: 88 },
{ id: 2, name: "jeffrey", utility: 102 }
]
});
const wrapper = mount(<AsyncComponent />);
// NOW WHAT? This is where I'm stuck.
});
});
/*
*假设记录如下所示:
*{id:number,name:string,utility:number}
*/
//asyncComponent.js
类AsyncComponent扩展了React.Component{
建造师(道具){
超级(道具);
此.state={
记录:[]
};
}
componentDidMount(){
//假设我正在使用像‘superagent’这样的库来进行返回承诺的ajax调用
获取('/some/url/that/returns/my/data')。然后((data)=>{
这是我的国家({
记录:数据记录
});
});
}
render(){
返回(
{this.\u renderList()}
);
}
_renderList(){
返回此.state.records.map((记录)=>{
返回(
{record.name}
{record.utility}
);
});
}
}
//asyncComponentTests.js
描述(“异步组件测试”,()=>{
它(“应该在componentDidMount中的setState执行后正确呈现”,()=>{
//我正在考虑使用类似'nock'的库来模拟http请求
诺克(“http://some.url.com")
.get(“/some/url/that/returns/my/data”)
.答复(200{
数据:[
{id:1,名称:“willson”,实用程序:88},
{id:2,名称:“jeffrey”,实用程序:102}
]
});
const wrapper=mount();
//现在怎么办?这就是我被困的地方。
});
});
忽略理智的建议,重新思考结构,一种方法是:
- 模拟请求(与sinon进行外汇交易),使其返回对某些记录的承诺
- 使用酶的
功能mount
- 断言该州还没有您的记录
- 让您的rest函数使用
回调done
- 稍等一下(使用
进行外汇兑换),这将确保您的承诺得到解决setImmediate
- 再次在已安装的组件上断言,这一次检查状态是否已设置
- 调用已完成回调以通知测试已完成
// asyncComponentTests.js
describe("Async Component Tests", () => {
it("should render correctly after setState in componentDidMount executes", (done) => {
nock("http://some.url.com")
.get("/some/url/that/returns/my/data")
.reply(200, {
data: [
{ id: 1, name: "willson", utility: 88 },
{ id: 2, name: "jeffrey", utility: 102 }
]
});
const wrapper = mount(<AsyncComponent />);
// make sure state isn't there yet
expect(wrapper.state).to.deep.equal({});
// wait one tick for the promise to resolve
setImmediate(() => {
expect(wrapper.state).do.deep.equal({ .. the expected state });
done();
});
});
});
//asyncComponentTests.js
描述(“异步组件测试”,()=>{
它(“应在componentDidMount中的setState执行后正确呈现”,(完成)=>{
诺克(“http://some.url.com")
.get(“/some/url/that/returns/my/data”)
.答复(200{
数据:[
{id:1,名称:“willson”,实用程序:88},
{id:2,名称:“jeffrey”,实用程序:102}
]
});
const wrapper=mount();
//确保州还没到
expect(wrapper.state).to.deep.equal({});
//等待承诺的解决
setImmediate(()=>{
expect(wrapper.state.do.deep.equal({..the expected state});
完成();
});
});
});
注:
我对nock没有任何线索,因此我在这里假设您的代码是正确的。实际上,这是一个常见的问题,由于承诺和
componentDidMount
,它显得更加复杂:
您正在尝试测试仅在另一个函数范围内定义的函数。i、 你应该把你的功能分开,分别测试
组件
class AsyncComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
records: []
};
}
componentDidMount() {
request.get('/some/url/that/returns/my/data')
.then(this._populateState);
}
render() {
return (
<div className="async_component">
{ this._renderList() }
</div>
);
}
_populateState(data) {
this.setState({
records: data.records
});
}
_renderList() {
return this.state.records.map((record) => {
return (
<div className="record">
<p>{ record.name }</p>
<p>{ record.utility }</p>
</div>
);
});
}
}
// asyncComponentTests.js
describe("Async Component Tests", () => {
describe("componentDidMount()", () => {
it("should GET the user data on componentDidMount", () => {
const data = {
records: [
{ id: 1, name: "willson", utility: 88 },
{ id: 2, name: "jeffrey", utility: 102 }
]
};
const requestStub = sinon.stub(request, 'get').resolves(data);
sinon.spy(AsyncComponent.prototype, "_populateState");
mount(<AsyncComponent />);
assert(requestStub.calledOnce);
assert(AsyncComponent.prototype._populateState.calledWith(data));
});
});
describe("_populateState()", () => {
it("should populate the state with user data returned from the GET", () => {
const data = [
{ id: 1, name: "willson", utility: 88 },
{ id: 2, name: "jeffrey", utility: 102 }
];
const wrapper = shallow(<AsyncComponent />);
wrapper._populateState(data);
expect(wrapper.state).to.deep.equal(data);
});
});
});
类AsyncComponent扩展了React.Component{
建造师(道具){
超级(道具);
此.state={
记录:[]
};
}
componentDidMount(){
get('/some/url/that/returns/my/data')
.然后(这一点);
}
render(){
返回(
{this.\u renderList()}
);
}
_填充测试(数据){
这是我的国家({
记录:数据记录
});
}
_renderList(){
返回此.state.records.map((记录)=>{
返回(
{record.name}
{record.utility}
);
});
}
}
单元测试
class AsyncComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
records: []
};
}
componentDidMount() {
request.get('/some/url/that/returns/my/data')
.then(this._populateState);
}
render() {
return (
<div className="async_component">
{ this._renderList() }
</div>
);
}
_populateState(data) {
this.setState({
records: data.records
});
}
_renderList() {
return this.state.records.map((record) => {
return (
<div className="record">
<p>{ record.name }</p>
<p>{ record.utility }</p>
</div>
);
});
}
}
// asyncComponentTests.js
describe("Async Component Tests", () => {
describe("componentDidMount()", () => {
it("should GET the user data on componentDidMount", () => {
const data = {
records: [
{ id: 1, name: "willson", utility: 88 },
{ id: 2, name: "jeffrey", utility: 102 }
]
};
const requestStub = sinon.stub(request, 'get').resolves(data);
sinon.spy(AsyncComponent.prototype, "_populateState");
mount(<AsyncComponent />);
assert(requestStub.calledOnce);
assert(AsyncComponent.prototype._populateState.calledWith(data));
});
});
describe("_populateState()", () => {
it("should populate the state with user data returned from the GET", () => {
const data = [
{ id: 1, name: "willson", utility: 88 },
{ id: 2, name: "jeffrey", utility: 102 }
];
const wrapper = shallow(<AsyncComponent />);
wrapper._populateState(data);
expect(wrapper.state).to.deep.equal(data);
});
});
});
//asyncComponentTests.js
描述(“异步组件测试”,()=>{
描述(“componentDidMount()”,()=>{
它(“应该在componentDidMount上获取用户数据”,()=>{
常数数据={
记录:[
{id:1,名称:“willson”,实用程序:88},
{id:2,名称:“jeffrey”,实用程序:102}
]
};
const requestStub=sinon.stub(请求'get')。解析(数据);
sinon.spy(AsyncComponent.prototype,“\u populateState”);
mount();
断言(requestStub.calledOnce);
断言(AsyncComponent.prototype.\u populateState.calledWith(data));
});
});
描述(“_populateState()”,()=>{
它(“应该用GET返回的用户数据填充状态”,()=>{
常数数据=[
{id:1,名称:“willson”,实用程序:88},
{id:2,名称:“jeffrey”,实用程序:102}
];
常量包装器=浅();
包装器。填充测试(数据);
expect(wrapper.state).to.deep.equal(data);
});
});
});
注意:我仅从文档中编写了单元测试,因此使用浅层
、装载
、断言
、和预期
可能不是最佳实践。那么,您认为呢