简体   繁体   English

在 componentDidMount 中获取时如何测试反应组件?

[英]How to test react components when fetch in componentDidMount?

I have component where I perform two fetch operations in componentDidMount .我有一个组件,我在componentDidMount中执行两个fetch操作。 I want to test this and I have to be honest: it's not clear to me how to proceed at all.我想对此进行测试,而且我必须说实话:我根本不清楚如何进行。

The point is that it seems that there isn't a "standard" way to proceed.关键是似乎没有一个“标准”的方式来进行。 Essentially what I find more confusing is:本质上,我发现更令人困惑的是:

  • How to mock: apparently, you can mock asynchronous calls using Jest or jest-fetch-mock .如何模拟:显然,您可以使用Jestjest-fetch-mock模拟异步调用。
  • How to manage the lifecycle and structure the tests:如何管理生命周期和构建测试:
    • In this article in Approach 3 is explained how to implement a test using enzyme and jest-fetch-mock.在方法 3 的这篇文章中,解释了如何使用酶和 jest-fetch-mock 实现测试。
    • In this article it is explained how to test async calls with jest only.本文中,解释了如何仅使用 jest 测试异步调用。

It's not very clear to me when I should use one approach/library instead of the other.我不太清楚什么时候应该使用一种方法/库而不是另一种。

This is a simplified version of my function:这是我的功能的简化版本:

componentDidMount() {
    fetch(URL, {
        method: 'GET',
    }).then(response => {
        if (response.ok) {
            return response.json();
        } else {
            throw new Error("Error loading data from " + URL);
        }
    }).then(data => {
        if (!_.isEmpty(data)) {
            this.setState({
                data: data,
            });
        } else {
            throw new Error("Invalid data from " + URL);
        }
    }).catch(error => {
        console.log(URL + ' error: ', error);
        this.setState({error});
    });

    const payload = {...};

    fetch(URL2, {
        method: 'POST',
        body: JSON.stringify(payload),
    }).then(response => {
        if (response.ok) {
            return response.json();
        } else {
            throw new Error("Error loading data from " + URL2);
        }
    }).then(data => {
        if (!_.isEmpty(data2)) {
            this.setState({
                data2: data2
            });
        } else {
            throw new Error("Invalid data from " + URL2);
        }

    }).catch(error => {
        this.setState({error, isLoading: false});
    });

} }

What I want to test is:我要测试的是:

  • Supposing that the fetch ( GET ) went well, test the updated state respect the form that I want (so both case where data is well/bad formed).假设获取 ( GET ) 进行顺利,请测试更新后的state是否符合我想要的形式(因此这两种情况下data格式正确/错误)。
  • Supposing that fetching fails, test that state is updated with error假设抓取失败,测试state更新error
  • Notice that I want to perform similar tests for the second fetch too.请注意,我也想对第二次提取执行类似的测试。

Of course I will need a mocking mechanism to mock the two answers (for GET and POST operations) but is not clear how should I do it, or how to test the results.当然,我需要一个模拟机制来模拟这两个答案(对于GETPOST操作),但不清楚我应该怎么做,或者如何测试结果。

You don't need to mock the api call.您不需要模拟 api 调用。 fetch has its own library tests so you don't need to test if fetch works. fetch有自己的库测试,所以你不需要测试fetch是否有效。 But if you really need to test your method, you can just use jest - https://facebook.github.io/jest/docs/en/asynchronous.html .但是如果你真的需要测试你的方法,你可以只使用jest - https://facebook.github.io/jest/docs/en/asynchronous.html Forget the jest-fetch-mock.忘记 jest-fetch-mock。 You can test:你可以测试:

  1. Was the method componentDidMount called?是否调用了componentDidMount方法?
  2. Was yourMethod called?你的方法被yourMethod吗?
  3. After yourMethod finished, did the change occurred?yourMethod完成后,是否发生了变化? (Your new state is the expected one?) (你的新状态是预期的吗?)

Just remember not to test the libraries themselves, or to go very deep into the component tree.请记住不要测试库本身,或者深入组件树。 You should only test atomically.您应该只进行原子测试。 One thing at a time.一心一意。

Now:现在:

You can use async/await or just test the fetch itself.您可以使用async/await或仅测试获取本身。 First of all, you should abstract those fetch 'es to their own methods.首先,您应该将那些fetch抽象为它们自己的方法。 Now.现在。 If all you do is concatenate promises and if u get everything correctly u set the state, you just need to, on the test file, resolve that promise, and on its callback, check if the state changed to what you wanted.如果您所做的只是连接 promises,并且如果您正确地设置了状态,那么您只需要在测试文件上解决该 promise,并在其回调中检查状态是否更改为您想要的状态。

Again, this has all you need to know: https://facebook.github.io/jest/docs/en/asynchronous.html#promises同样,这包含您需要了解的所有信息: https ://facebook.github.io/jest/docs/en/asynchronous.html#promises

And if you need one more resource here you go: https://codereviewvideos.com/course/react-redux-and-redux-saga-with-symfony-3/video/testing-javascript-s-fetch-with-jest-happy-path如果您需要更多资源,请访问: https ://codereviewvideos.com/course/react-redux-and-redux-saga-with-symfony-3/video/testing-javascript-s-fetch-with-jest -快乐之路

The trick here is to assert state/snapshot after the data from remote source are received.这里的技巧是在接收到来自远程源的数据后断言状态/快照。 Even with mocking it's still goes asynchronously.即使有模拟,它仍然是异步的。 So you can use eg setTimeout to postpone assertion:所以你可以使用例如 setTimeout 来推迟断言:

import React from "react";
import { shallow } from "enzyme";
import sinon from "sinon";
import fetch from "node-fetch";

sinon.stub(fetch, "Promise").returns(
  Promise.resolve({
    json: () => Promise.resolve( { name: "Hello" } )
  })
);


class Test extends React.Component {
  state = {
    name: "none"
  };
  async componentDidMount() {
    const res = await fetch( "https://swapi.co/api/people/1" ),
          data = await res.json();
    this.setState({ name: data.name });
  }
  render() {
    return <h1>{ this.state.name }</h1>;
  }
}

describe( "component with fetch", () => {
  test( "state gets updated with the fetch", ( done ) => {
    const wrapper = shallow( <Test /> );
    setTimeout(() => {
      wrapper.update();
      const state = wrapper.instance().state;
      console.log(state);
      done();
    }, 10 );
  });

});

We generally test the state that the lifecycle methods have changed by mocking the fetch calls.我们通常通过模拟获取调用来test the state Avoid using setTimeout in tests as you never know how much time the fetchMock is gonna take, so you can use await instead of that. Avoid using setTimeout ,因为您永远不知道 fetchMock 将花费多少时间,因此您可以使用await代替它。 For example:例如:

import React from "react";
import {shallow} from "enzyme";
import fetchMock from "fetch-mock";
import TestComponent from "./TestComponent";

describe(() => {
    it("should set the state after fetching data", () => {
        // arrange
        fetchMock.get(`https://www.example.com`, mockResponse);
        const wrapper = shallow(<TestComponent>);
        
        // act
        await wrapper.instance().componentDidMount();

        // assert
        expect(wrapper.state()).toEqual(newStateObject);

    })

})

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

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