Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/reactjs/24.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Reactjs 如何在react组件中测试api调用,并在api调用成功后预期视图更改?_Reactjs_Jestjs_Enzyme - Fatal编程技术网

Reactjs 如何在react组件中测试api调用,并在api调用成功后预期视图更改?

Reactjs 如何在react组件中测试api调用,并在api调用成功后预期视图更改?,reactjs,jestjs,enzyme,Reactjs,Jestjs,Enzyme,我有一个简单的react组件,只有一个按钮,当单击该按钮时,它使用fetch进行api调用,在成功调用之后,调用setState来更新组件 在my-button.jsx文件中 import React from "react"; export default class MyButton extends React.Component { constructor(props) { super(props); this.state = {

我有一个简单的react组件,只有一个按钮,当单击该按钮时,它使用fetch进行api调用,在成功调用之后,调用setState来更新组件

在my-button.jsx文件中

import React from "react";

export default class MyButton extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            user: null
        }
        this.getUser = this.getUser.bind(this);
    }

    async getUser() {
        try {
            const res = await fetch("http://localhost:3000/users");
            if (res.status >= 400)
                throw new Error("something went wrong");

            const user = await res.json();
            this.setState({ user });

        } catch (err) {
            console.error(err);
        }


    }

    render() {

        return (
            <div>
                <button onClick={this.getUser}>Click Me</button>
                {this.state.user ? <p>got user</p> : null}
            </div>
        )
    }


}

使用settimeout将对测试期望强制执行此顺序

getUser
→ <代码>测试预期

在当前的
MyButton
实现中,没有一种直接的方法来实现这一点
getUser
需要被提取出来并作为道具传递给
MyButton
,以便在承诺链上有更好的控制,即
getUser

样本

getUser().then(testExpectations)
在重构的第一步中,在按钮
onClick
中调用
getUser
,代替对组件的
shallowRapper
的模拟调用

这是simulate所做的,但它返回一个包装器实例。你不想要这个;您希望从调用
getUser
返回承诺,以便链接到它

it("must test the button click", (done) => {
  fetch.mockImplementation(() => {
    return Promise.resolve({
      status: 200,
      json: () => Promise.resolve({ name: "Manas", userId: 2 })
    });
  });

  const wrapper = shallow(<MyButton />);
  const button = wrapper.find("button");
  const onClick = button.prop('onClick');

  onClick().then(() => {
    wrapper.update();
    expect(wrapper.find("p").length).toBe(1);
    fetch.mockClear();
    done();
  })
})
it(“必须测试按钮点击”,(完成)=>{
fetch.mockImplementation(()=>{
还愿({
现状:200,
json:()=>Promise.resolve({name:“Manas”,userId:2})
});
});
常量包装器=浅();
const button=wrapper.find(“按钮”);
const onClick=button.prop('onClick');
onClick()。然后(()=>{
wrapper.update();
expect(wrapper.find(“p”).length).toBe(1);
fetch.mockClear();
完成();
})
})

重构的下一步是将
getUser
作为属性转发到
MyButton
。如果您发现
MyButton
总是将该特定实现用于其click事件处理程序,则可能不需要这样做。

如果在测试运行期间有异步调用,则必须在事件循环结束时运行断言/期望

it('must test the button click', done => {
  fetch.mockImplementation(() => {
    return Promise.resolve({
      status: 200,
      json: () => {
        return Promise.resolve({ name: 'Manas', userId: 2 });
      }
    });
  });
  const wrapper = shallow(<MyButton />);

  wrapper.find('button').simulate('click'); // async invocation

  // wait till async action is done
  new Promise((resolve, reject) => {
    setImmediate(() => {
      resolve();
    }, 0);
  }).then(() => {
    wrapper.update(); // you probably won't need this line
    expect(wrapper.find('p').length).toBe(1);
    fetch.mockClear();
    done();
  });
});
it('必须测试按钮点击',完成=>{
fetch.mockImplementation(()=>{
还愿({
现状:200,
json:()=>{
返回Promise.resolve({name:'Manas',userId:2});
}
});
});
常量包装器=浅();
wrapper.find('button').simulate('click');//异步调用
//等待异步操作完成
新承诺((解决、拒绝)=>{
setImmediate(()=>{
解决();
}, 0);
}).然后(()=>{
wrapper.update();//您可能不需要这一行
expect(wrapper.find('p').length).toBe(1);
fetch.mockClear();
完成();
});
});
在我的所有项目中,我通常将其作为一个util方法编写出来

// test-helper.js
export const waitForAsyncActionsToFinish = () => {
  return new Promise((resolve, reject) => {
    setImmediate(() => {
      resolve();
    }, 0);
  });
};

it('test something', (done) => {
  // mock some async actions
  const wrapper = shallow(<Component />);

  // componentDidMount async actions
  waitForAsyncActionsToFinish().then(() => {
    wrapper.find('.element').simulate('click');

    // onClick async actions - you have to wait again
    waitForAsyncActionsToFinish().then(() => {
      expect(wrapper.state.key).toEqual('val');
      done();
    });
  });
});
//test-helper.js
导出常量waitForAsyncActionsToFinish=()=>{
返回新承诺((解决、拒绝)=>{
setImmediate(()=>{
解决();
}, 0);
});
};
它('测试某物',(完成)=>{
//模拟一些异步操作
常量包装器=浅();
//componentDidMount异步操作
waitForAsyncActionsToFinish()。然后(()=>{
wrapper.find('.element').simulate('click');
//onClick异步操作-您必须再次等待
waitForAsyncActionsToFinish()。然后(()=>{
expect(wrapper.state.key).toEqual('val');
完成();
});
});
});

如果不使用settimeout会发生什么情况?测试失败预期1收到0如果没有“settimeout”,您可以共享您收到的错误吗?我已经用失败的测试更新了它,但我的测试失败预期1收到相同的错误0Hmm,让我调查这要求代码的所有异步部分(模拟为)使用等于0的超时进行解析,以便最后解析由
waitForAsyncActionsToFinish返回的承诺。模拟并立即返回所有异步操作可能并不总是合适的,例如,当渲染包括动画,并且需要延迟以检查结果时。
setImmediate
在事件循环结束时添加新的异步调用。因此,基本上我们在所有异步调用完成后解决。但你是对的,这可能不适用于超时。这就是我们开始使用jest计时器或sinon计时器的时候。OPs问题更侧重于无超时的异步操作。这应该可以解决OP面临的问题。你的回答我觉得有点不对劲,它只是用setImmediate替换了setTimeout
it('must test the button click', done => {
  fetch.mockImplementation(() => {
    return Promise.resolve({
      status: 200,
      json: () => {
        return Promise.resolve({ name: 'Manas', userId: 2 });
      }
    });
  });
  const wrapper = shallow(<MyButton />);

  wrapper.find('button').simulate('click'); // async invocation

  // wait till async action is done
  new Promise((resolve, reject) => {
    setImmediate(() => {
      resolve();
    }, 0);
  }).then(() => {
    wrapper.update(); // you probably won't need this line
    expect(wrapper.find('p').length).toBe(1);
    fetch.mockClear();
    done();
  });
});
// test-helper.js
export const waitForAsyncActionsToFinish = () => {
  return new Promise((resolve, reject) => {
    setImmediate(() => {
      resolve();
    }, 0);
  });
};

it('test something', (done) => {
  // mock some async actions
  const wrapper = shallow(<Component />);

  // componentDidMount async actions
  waitForAsyncActionsToFinish().then(() => {
    wrapper.find('.element').simulate('click');

    // onClick async actions - you have to wait again
    waitForAsyncActionsToFinish().then(() => {
      expect(wrapper.state.key).toEqual('val');
      done();
    });
  });
});