1

我正在尝试为我的集成测试中的网络获取运行 Jest 测试。它使用 beforeEach 创建了一个虚假的网络获取响应,它应该返回一个长度为 2 的响应。当我从以下代码中删除 done 时,测试运行良好,但是只要我用作done回调并且似乎测试失败并且错误表明只返回了 1 的长度,而预期的长度应该是 2。

它使用 Enzyme 和 full dom 来测试 3 个组件的集成,我不使用的时候测试很好done,但是我一使用done它就失败了。

beforeEach(() => {
    moxios.install();
    moxios.stubRequest('http://jsonplaceholder.typicode.com/comments', {
        status: 200,
        response: [ { name: 'Fetch #1' }, { name: 'Fetch #2' } ]
    });
});

afterEach(() => {
    moxios.uninstall();
});

it('can fetch a list of comments and display them', (done) => {
    // Attempt to render the *entire* app
    const wrapped = mount(
        <Root>
            <App />
        </Root>
    );
    // find the 'fetchComments' button and click it
    wrapped.find('.fetch_comments').simulate('click');

    // setTimeout is used because moxio introduces a little delay fetching the data.
    // so setTimeout makes Jest to have a little delay so it won't throw error.
    //  Expect to find a list of comments!
    //
    setTimeout(() => {
        wrapped.update();
        console.log(wrapped.find('li').length);
        expect(wrapped.find('li').length).toEqual(2);

        wrapped.unmount();
        done();
    }, 3300);

});
      1
    console.error node_modules/jsdom/lib/jsdom/virtual-console.js:29
      Error: Uncaught [Error: expect(received).toEqual(expected) // deep equality

      Expected: 2
      Received: 1]
          at reportException (/Users/dmml/Documents/Developer/reactPractice/testing/testing-stephen-grider/node_modules/jsdom/lib/jsdom/living/helpers/runtime-script-errors.js:66:24)
          at Timeout.callback [as _onTimeout] (/Users/dmml/Documents/Developer/reactPractice/testing/testing-stephen-grider/node_modules/jsdom/lib/jsdom/browser/Window.js:680:7)
          at listOnTimeout (internal/timers.js:531:17)
          at processTimers (internal/timers.js:475:7) JestAssertionError: expect(received).toEqual(expected) // deep equality

      Expected: 2
      Received: 1
          at toEqual (/Users/dmml/Documents/Developer/reactPractice/testing/testing-stephen-grider/src/__tests__/integrations.test.js:36:37)
          at Timeout.callback [as _onTimeout] (/Users/dmml/Documents/Developer/reactPractice/testing/testing-stephen-grider/node_modules/jsdom/lib/jsdom/browser/Window.js:678:19)
          at listOnTimeout (internal/timers.js:531:17)
          at processTimers (internal/timers.js:475:7) {
        matcherResult: {
          actual: 1,
          expected: 2,
          message: [Function],
          name: 'toEqual',
          pass: false
        }
      }

  ● can fetch a list of comments and display them

    expect(received).toEqual(expected) // deep equality

    Expected: 2
    Received: 1

      34 |              wrapped.update();
      35 |              console.log(wrapped.find('li').length);
    > 36 |              expect(wrapped.find('li').length).toEqual(2);
         |                                                ^
      37 | 
      38 |              wrapped.unmount();
      39 |              done();

      at toEqual (src/__tests__/integrations.test.js:36:37)
      at Timeout.callback [as _onTimeout] (node_modules/jsdom/lib/jsdom/browser/Window.js:678:19)

Test Suites: 1 failed, 2 passed, 3 total
Tests:       1 failed, 5 passed, 6 total
Snapshots:   0 total
Time:        5.028s
Ran all test suites related to changed files.
4

1 回答 1

0

我从来没有取得过很好的成功moxios。因此,我更喜欢mock-axios-adapter。如果您正在使用,您可以在类字段和响应thenables中返回调用。否则,如果您使用,您可以简单地使用类字段实例。此外,我尽量避免使用它,因为它会增加整体测试运行时间。axiosawaitasync/awaitPromise.resolve()setTimeout

例如 ( thenables):

class Example extends Component {
  state = {
    data: [],
    hasError: "",
    isLoading: true
  };

  componentDidMount = () => {
    window.setTimeout(() => {
      this.fetchData("users");
    }, 1500);
  };

  fetchData = url => {
    return app
      .get(`${url}`)
      .then(res => {
        this.setState({ isLoading: false, data: res.data });
      })
      .catch(err =>
        this.setState({ isLoading: false, hasError: err.toString() })
      );
  }

  handleClick = () => {
    this.setState({ isLoading: true, data: [] }, () => {
      this.fetchData("todos");
    });
  };

  render = () => (
    <div className="app-container">
      {this.state.isLoading ? (
        <ShowLoading />
      ) : this.state.hasError ? (
        <ShowError error={this.state.hasError} />
      ) : (
        <ShowData data={this.state.data} handleClick={this.handleClick} />
      )}
    </div>
  );
}

例如 ( async/await):

class Example extends Component {
  state = {
    data: [],
    hasError: "",
    isLoading: true
  };

  componentDidMount = () => {
    window.setTimeout(() => {
      this.fetchData("users");
    }, 1500);
  };

  fetchData = async url => {
    try {
      const res = await app.get(`${url}`);
      this.setState({ isLoading: false, data: res.data });
    } catch (err) {
      this.setState({ isLoading: false, hasError: err.toString() });
    }
  };

  handleClick = () => {
    this.setState({ isLoading: true, data: [] }, () => {
      this.fetchData("todos");
    });
  };

  render = () => (
    <div className="app-container">
      {this.state.isLoading ? (
        <ShowLoading />
      ) : this.state.hasError ? (
        <ShowError error={this.state.hasError} />
      ) : (
        <ShowData data={this.state.data} handleClick={this.handleClick} />
      )}
    </div>
  );
}

然后在您的测试中调用和解析fetchData(适用于这两个示例——我使用这个词implementation来指代虚假 API 请求如何与组件交互,并integration指代真实 API如何与组件交互) - 但这都是主观的):

import React from "react";
import MockAdapter from "axios-mock-adapter";
import { shallow } from "enzyme"; // I use shallow, but you can use mount
import Users from "../index.js";
import app from "../../../utils/axiosConfig";

const mockAxios = new MockAdapter(app);

const data = [
  {
    id: 1,
    name: "Leanne Graham",
    username: "Bret",
    email: "Sincere@april.biz",
    address: {
      street: "Kulas Light",
      suite: "Apt. 556",
      city: "Gwenborough",
      zipcode: "92998-3874",
      geo: {
        lat: "-37.3159",
        lng: "81.1496"
      }
    },
    phone: "1-770-736-8031 x56442",
    website: "hildegard.org",
    company: {
      name: "Romaguera-Crona",
      catchPhrase: "Multi-layered client-server neural-net",
      bs: "harness real-time e-markets"
    }
  }
];

describe("App", () => {
  let wrapper;
  beforeEach(() => {
    mockAxios.reset();
    wrapper = shallow(<Users />);
  });

  afterAll(() => {
    wrapper.unmount();
  });

  it("renders without errors", () => {
    expect(wrapper.find("div.app-container")).toHaveLength(1);
  });

  it("initally shows that it's loading", () => {
    expect(wrapper.state("isLoading")).toBeTruthy();
    expect(wrapper.find("ShowLoading")).toHaveLength(1);
  });

  describe("API Implementation", () => {
    it("renders data and shows an Update button", async () => {
      mockAxios.onGet("users").reply(200, data);
      await Promise.resolve(wrapper.instance().fetchData("users"));

      expect(wrapper.state("isLoading")).toBeFalsy();
      expect(
        wrapper
          .find("ShowData")
          .dive()
          .find("div.data")
      ).toHaveLength(1);
      expect(
        wrapper
          .find("ShowData")
          .dive()
          .find("button.update")
      ).toHaveLength(1);
      mockAxios.restore();
    });
  });

  describe("API Integration", () => {
    it("retrieves real data from API, renders data, and shows an Update button", async () => {
      await Promise.resolve(wrapper.instance().fetchData("users"));

      expect(wrapper.state("isLoading")).toBeFalsy();
      expect(
        wrapper
          .find("ShowData")
          .dive()
          .find("div.data")
      ).toHaveLength(10);
      expect(
        wrapper
          .find("ShowData")
          .dive()
          .find("button.update")
      ).toHaveLength(1);
    });
  });
});

运行Tests旁边的选项卡Browser

工作示例(thenables):

使用 Axios 模拟测试 (thenables) 编辑 API 示例

工作示例(异步/等待):

使用 Axios 模拟测试 (async/await) 编辑 API 示例

于 2019-09-05T01:37:31.290 回答