如何通过玩笑和酶测试具有异步功能的React组件

问题描述 投票:-1回答:1

我有一个组件:

RandomGif.js

import React, { Component } from "react";
import Gif from "./Gif";
import Loader from "./library/Loader";
import { fetchRandom } from "../resources/api";

class RandomGif extends Component {
  constructor(props) {
    super(props);

    this.handleClick = this.handleClick.bind(this);
  }

  state = {
    loading: false,
    gif: null
  };

  componentDidMount() {
    this.handleClick();
  }

  async handleClick() {
    let gifContent = null;

    try {
      this.setState({
        loading: true
      });

      const result = await fetchRandom();

      if (!!result && result.data) {
        gifContent = {
          id: result.data.id,
          imageUrl: result.data.images.downsized_large.url,
          staticImageUrl: result.data.images.downsized_still.url,
          title: result.data.title
        };
      }
    } catch (e) {
      console.error(e);
    } finally {
      this.setState({
        loading: false,
        gif: gifContent
      });
    }
  }

  render() {
    const { gif, loading } = this.state;

    const showResults = gif && !loading;

    return (
      <div className="random">
        {!showResults && <Loader />}

        <button className="btn" onClick={this.handleClick}>
          RANDOMISE
        </button>

        {showResults && <Gif data={gif} />}
      </div>
    );
  }
}

export default RandomGif;

如果直接从该组件的实例调用方法,则可以成功测试状态是否正在更新。但是,如果我模拟按钮单击,则不会更新任何内容,并且测试失败。我已经尝试过setImmediatesetTimeout技巧,但是这些技巧不起作用。

到目前为止,我还无法编写测试用例:

  • 模拟按钮单击。
  • 模拟生命周期方法。

这是我到目前为止提出的。

RandomGif.spec.js

import React from "react";
import { shallow, mount } from "enzyme";
import RandomGif from "./RandomGif";

describe("Generate Random Gif", () => {
  it("should render correctly.", () => {
    const wrapper = shallow(<RandomGif />);

    expect(wrapper).toMatchSnapshot();
  });

  it("should load a random GIF on calling handleSearch fn.", async () => {
    const wrapper = mount(<RandomGif />);
    const instance = wrapper.instance();

    expect(wrapper.state("gif")).toBe(null);

    await instance.handleClick();

    expect(wrapper.state("gif")).not.toBe(null);
  });

  it("THIS TEST FAILS!!!", () => {
    const wrapper = mount(<RandomGif />);

    expect(wrapper.state("gif")).toBe(null);

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

    expect(wrapper.state("gif")).not.toBe(null);
  });
});

[请帮助我找出称为“前端测试”的难题的缺失部分。

reactjs jestjs enzyme
1个回答
0
投票
  1. 我们需要mock fetchRandom,因此在测试期间不会发送任何实际请求。
fetchRandom
  1. 由于模拟是一个Promise(已解决-但仍然可以保证),我们需要import { fetchRandom } from "../resources/api"; jest.mock("../resources/api"); // due to automocking fetchRandom is jest.fn() // somewhere in the it() fetchRandom.mockReturnValue(Promise.resolve({ data: { images: ..., title: ..., id: ...} })) setTimeout才能使组件的代码实现此Promise已被解决。全部与await <anything>有关。
microtasks/macrotasks queue

wrapper.find('button').simulate('click');
await Promise.resolve();
// component has already been updated here

顺便说一下,您已经做到了

it("test something" , (done) => {
wrapper.find('button').simulate('click');
setTimeout(() => {
  // do our checks on updated component
  done();  
}); // 0 by default, but it still works

})

但是对我来说,它看起来和说的一样神奇

await instance.handleClick();

此外,它还可以工作(查看微任务/宏任务上的链接),我认为这会使测试的可读性更差(“ handleClick返回需要什么等待我们处理?”)。因此,我建议使用繁琐但不太混乱的await 42; 甚至await Promise.resolve();

  1. 引用await undefined;和直接调用实例方法都是反模式。只是一个引号(由state我完全同意):

总而言之,如果您的测试使用instance()或state(),请知道您正在测试用户可能不了解甚至不在乎的事情,这将使您对事情有所信心,从而使测试更进一步当您的用户使用它们时将起作用。

让我们来检查渲染结果:

Kent C. Dodds

让我们完全明白:

import Loader from "./library/Loader";

...
wrapper.find('button').simulate('click');
expect(wrapper.find(Loader)).toHaveLength(1);
await Promise.resolve();
expect(wrapper.find(Loader)).toHaveLength(1);
expect(wrapper.find(Gif).prop("data")).toEqual(data_we_mocked_in_mock)
© www.soinside.com 2019 - 2024. All rights reserved.