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

时间:2018-02-27 12:14:19

标签: reactjs unit-testing jestjs enzyme

我有一个组件可以在fetch中执行两个componentDidMount操作。我想测试一下,我必须说实话:我不清楚如何继续进行。

关键是似乎没有“标准”的方式来进行。基本上我发现更令人困惑的是:

  • 如何模拟:显然,您可以使用Jestjest-fetch-mock模拟异步调用。
  • 如何管理生命周期并构建测试:
    • 在方法3的this文章中解释了如何使用酶和jest-fetch-mock实现测试。
    • this文章中,解释了如何仅使用jest测试异步调用。

当我应该使用一种方法/库而不是另一种时,我不是很清楚。

这是我的功能的简化版本:

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});
    });

}

我想测试的是:

  • 假设fetch(GET)运行良好,请测试更新的state尊重我想要的表单(因此data形成良好/不良的情况都是如此)。
  • 假设抓取失败,请使用state
  • 测试error是否已更新
  • 请注意,我也想为第二次获取执行类似的测试。

当然我需要一个模拟机制来模拟两个答案(对于GETPOST操作)但不清楚我该怎么做,或者如何测试结果。

3 个答案:

答案 0 :(得分:2)

您无需模拟api调用。 fetch有自己的库测试,因此您无需测试fetch是否有效。但是,如果您确实需要测试方法,可以使用jest - https://facebook.github.io/jest/docs/en/asynchronous.html。忘掉jest-fetch-mock。你可以测试:

  1. 方法componentDidMount是否被调用?
  2. yourMethod被叫了吗?
  3. yourMethod完成后,是否发生了更改? (你的新州是预期的州吗?)
  4. 请记住,不要自己测试库,也不要深入到组件树中。你应该只进行原子测试。一次一件事。

    现在:

    您可以使用async/await或只测试获取本身。首先,您应该将fetch'es抽象为他们自己的方法。现在。如果您所做的只是连接承诺,并且如果您正确地获取所有内容,则设置状态,您只需要在测试文件上解析该承诺,并在其回调中检查状态是否已更改为您想要的状态。

    同样,您需要了解所有内容:https://facebook.github.io/jest/docs/en/asynchronous.html#promises

    如果您还需要一个以上的资源:https://codereviewvideos.com/course/react-redux-and-redux-saga-with-symfony-3/video/testing-javascript-s-fetch-with-jest-happy-path

答案 1 :(得分:0)

这里的技巧是在接收到来自远程源的数据后声明状态/快照。即使进行了模拟,它仍然是异步进行的。所以你可以使用例如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 );
  });

});

答案 2 :(得分:0)

我们通常test the state通过模拟提取调用已更改了生命周期方法。 Avoid using setTimeout在测试中,因为您永远不知道fetchMock将花费多少时间,因此您可以使用await代替它。例如:

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);

    })

})