简体   繁体   English

在 React 组件中测试 fetch() 方法

[英]Testing fetch() method inside React component

I have an App component that is responsible for rendering child input components, it is also responsible for handling fetch requests to the Twitch API via a method called channelSearch .我有一个 App 组件负责呈现子输入组件,它还负责通过一个名为channelSearch的方法处理对 Twitch API 的获取请求。 I have tried to adhere to suggested best practices outlined here for working with ajax/fetch with React.我已尝试遵循此处概述的建议最佳实践以便在 React 中使用 ajax/fetch。

The method is passed down through props and called via a callback.该方法通过 props 传递并通过回调调用。

Note the fetch method is actually isomorphic-fetch.注意 fetch 方法实际上是 isomorphic-fetch。

channelSearch (searchReq, baseUrl="https://api.twitch.tv/kraken/channels/") {
  fetch(baseUrl + searchReq)
  .then(response => {
    return response.json();
  })
  .then(json => {
    this.setState({newChannel:json});
  })
  .then( () => {
    if (!("error" in this.state.newChannel) && this.channelChecker(this.state.newChannel._id, this.state.channelList) ) {
      this.setState(
        {channelList: this.state.channelList.concat([this.state.newChannel])}
      );
    }
  })
  .catch(error => {
    return error;
  });
}

I am currently trying to write a test for the channelSearch method.我目前正在尝试为channelSearch方法编写测试。 I am currently using enzyme and jsdom to mount the entire <App> component in a DOM.我目前使用的酶和jsdom到mount整个<App>在DOM组件。 Find the child node with the callback, simulate a click (which should fire the callback) and check to see if the state of the component has been changed.找到带有回调的子节点,模拟单击(应该触发回调)并检查组件的状态是否已更改。 However, this does not seem to work.但是,这似乎不起作用。

I have also tried calling the method directly, however, I run into problems with this.state being undefined.我也尝试过直接调用该方法,但是,我遇到了this.state未定义的问题。

test('channel search method should change newChannel state', t => {
  const wrapper = mount(React.createElement(App));

  wrapper.find('input').get(0).value = "test";
  console.log(wrapper.find('input').get(0).value);
  wrapper.find('input').simulate("change");

  wrapper.find('button').simulate("click");

  console.log(wrapper.state(["newChannel"]));


});

I am really lost, I am not sure if the method itself is poorly written or I am not using the correct tools for the job.我真的很迷茫,我不确定方法本身是否写得不好,或者我没有使用正确的工具来完成这项工作。 Any guidance will be greatly appreciated.任何指导将不胜感激。

Update #1:更新 #1:

I included nock as recommended in comments, test now looks like this:我按照评论中的建议包含了 nock,现在测试如下所示:

test('channel search method should change newChannel state', t => {
  // Test object setup

  var twitch = nock('https://api.twitch.tv')
                .log(console.log)
                .get('/kraken/channels/test')
                .reply(200, {
                  _id: '001',
                  name: 'test',
                  game: 'testGame'
                });

  function checker() {
    if(twitch.isDone()) {
      console.log("Done!");
      console.log(wrapper.state(["newChannel"]));
    }
    else {
      checker();
    }
  }

  const wrapper = mount(React.createElement(App));
  wrapper.find('input').get(0).value = "test";
  wrapper.find('input').simulate("change");
  wrapper.find('button').simulate("click");

  checker();
});

This still does not seem to change the state of the component.这似乎仍然没有改变组件的状态。

fetch is asynchronous but you're testing synchronously, you need to either mock fetch with a synchronous mock or make the test asynchronous. fetch 是异步的,但您正在同步测试,您需要使用同步模拟来模拟 fetch 或使测试异步。

nock may work for you here.诺克在这里可能对你有用

I suggest you create a sample of your test using plnkr .我建议您使用plnkr创建一个测试样本

I agree with Tom that you're testing synchronously.我同意 Tom 的观点,即您正在同步测试。 It would of course be helpful to show off your actual component code (all of the relevant portions, like what calls channelSearch , or at the least describe it by saying eg " channelSearch is called by componentDidMount() ". You said:展示您的实际组件代码当然会有所帮助(所有相关部分,例如调用channelSearch ,或者至少通过说例如“ channelSearchcomponentDidMount()调用”来描述它。您说:

I run into problems with this.state being undefined.我遇到了 this.state 未定义的问题。

This is because this.setState() is asynchronous .这是因为this.setState()是异步的 This is for performance reasons, so that React can batch changes.这是出于性能原因,以便 React 可以批量更改。

I suspect you'll need to change your code that is currently:我怀疑您需要更改当前的代码:

.then(json => {
    this.setState({newChannel:json});
})

to:到:

.then(json => {
  return new Promise(function(resolve, reject) {
    this.setState({newChannel:json}, resolve);
  })
})

Note that your checker() method won't work.请注意,您的checker()方法将不起作用。 It's looping, but twitch.isDone() will never be true because it never has a chance to run.它在循环,但twitch.isDone()永远不会为真,因为它永远没有机会运行。 Javascript is single threaded, so your checker code will run continuously, not allowing anything else in between. Javascript 是单线程的,因此您的检查器代码将连续运行,不允许在两者之间进行任何其他操作。

If you set up the plnkr, I'll take a look.如果你设置了plnkr,我会看看。

Refactor out the fetch code from the component then pass it it to the component as a callback function in the properties.重构从组件中fetch代码,然后将其作为属性中的回调函数传递给组件。

export class Channel extends React.Component {
  componentDidMount() {
    this.props.searchFunction().then(data => this.setState(data));
  }

  render() {
    return <div>{this.state}</div>;
  }
}

Uage:你的年龄:

function channelSearch(name) {
  return fetch(`https://api.twitch.tv/kraken/search/channels?query=${name}`);
}

<Channel searchFunction={channelSearch} />

Now you can test the API functionality independently of the component.现在您可以独立于组件测试 API 功能。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM