安装时如何从酶实例中获取 HOC 包裹的组件功能?

d3v*_*kit 2 javascript reactjs jestjs enzyme

我有父 -> 子组件,子组件在componentDidMount. fetch 在父级之上的 redux 存储中设置状态,这会导致子组件以不同的方式显示。

子组件也在使用material-ui withStyles,因此它会在组件周围创建一个 HOC。

在我的测试中,我需要挂载父组件,找到子组件,然后看到 fetch 正确更改了状态并导致子组件更新。

到目前为止,我的解决方案是这样的:

  • 挂载父级,找到子级
  • 称呼 child.instance().fetchFunction().then(() => expect(..))

但是,调用instance()child返回HOC,所以我得到的错误:

child.instance(...).fetchFunction 不是函数

我见过的所有解决方案都使用shallowdive绕过 HOC,但是如果我使用,shallow我将不得不在测试中创建一个模拟存储,它实际上不会将其作为集成测试进行测试。

我可以测试单独的 fetch 调用,然后使用测试组件shallow并将 props 传递给它,就好像状态已更改一样,但这并不能证明它们都可以一起工作。

这是一个代码沙盒,我在其中重现了该问题:

编辑测试异步 componentDidMount

这是一些示例代码(基本上是代码和框):

App.js

import React from "react";
import Child from "./Child";

class App extends React.Component {
  render() {
    return <Child />;
  }
}

export default App;
Run Code Online (Sandbox Code Playgroud)

Child.js

import React from "react";
import { withStyles } from "@material-ui/core/styles";

const childStyles = {
  margin: 0
};

class Child extends React.Component {
  state = {
    groceries: [],
    errorStatus: ""
  };

  componentDidMount() {
    console.log("calling fetch");

    this.fetchCall();
  }

  fetchCall = () => {
    return fetch("/api/v1/groceries")
      .then(this.checkStatus)
      .then(this.parseJSON)
      .then(this.setStateFromData)
      .catch(this.setError);
  };

  checkStatus = results => {
    if (results.status >= 400) {
      console.log("bad status");

      throw new Error("Bad Status");
    }

    return results;
  };

  setError = () => {
    console.log("error thrown");

    return this.setState({ errorStatus: "Error fetching groceries" });
  };

  parseJSON = results => {
    console.log("parse json");

    return results.json();
  };

  setStateFromData = data => {
    console.log("setting state");

    return this.setState({ groceries: data.groceries });
  };

  render() {
    const { groceries } = this.state;

    return (
      <div id="app">
        {groceries.map(grocery => {
          return <div key={grocery.id}>{grocery.item}</div>;
        })}
      </div>
    );
  }
}

export default withStyles(childStyles)(Child);
Run Code Online (Sandbox Code Playgroud)

App.test.js

import Enzyme from "enzyme";
import Adapter from "enzyme-adapter-react-16";
import React from "react";
import { mount } from "enzyme";
import App from "./App";
import Child from "./Child";

Enzyme.configure({ adapter: new Adapter() });

const mockResponse = (status, statusText, response) => {
  return new window.Response(response, {
    status: status,
    statusText: statusText,
    headers: {
      "Content-type": "application/json"
    }
  });
};

describe("App", () => {
  describe("ChildApp componentDidMount", () => {
    it("sets the state componentDidMount", () => {
      console.log("starting test for 200");

      global.fetch = jest.fn().mockImplementation(() =>
        Promise.resolve(
          mockResponse(
            200,
            null,
            JSON.stringify({
              groceries: [{ item: "nuts", id: 10 }, { item: "greens", id: 3 }]
            })
          )
        )
      );

      const renderedComponent = mount(<App />);
      const childApp = renderedComponent.find(Child);

      childApp
        .instance()
        .fetchCall()
        .then(() => {
          console.log("finished test for 200");
          expect(childApp.state("groceries").length).toEqual(2);
        });
    });

    it("sets the state componentDidMount on error", () => {
      console.log("starting test for 500");

      window.fetch = jest
        .fn()
        .mockImplementation(() =>
          Promise.resolve(
            mockResponse(
              400,
              "Test Error",
              JSON.stringify({ status: 400, statusText: "Test Error!" })
            )
          )
        );

      const renderedComponent = mount(<App />);
      const childApp = renderedComponent.find(Child);
      childApp
        .instance()
        .fetchCall()
        .then(() => {
          console.log("finished test for 500");
          expect(childApp.state("errorStatus")).toEqual(
            "Error fetching groceries"
          );
        });
    });
  });
});
Run Code Online (Sandbox Code Playgroud)

d3v*_*kit 5

写完之后,我找到了答案,但我觉得这是值得分享的,因为我对此很困惑。

而不是使用app.find(Child)(组件构造函数),使用app.find('Child')(组件显示名称)。这将找到实际的组件,而不是 hoc 包装的组件。

用于查找的酶文档(选择器)