如何监视React功能组件内部的方法?

时间:2020-02-21 19:37:41

标签: reactjs jestjs react-testing-library

我有这个功能组件。

Search.js


function Search() {
  const [term, setTerm] = useState('sun');

  function handleOnChange(e) {
    if (!e.target.value) {
      return false;
    }
    setTerm(e.target.value);
    return true;
  }

  return <input type="text" onChange={handleOnChange} placeholder="Search" />
}

Search.test.js

import { render, fireEvent } from '@testing-library/react';
import Search from '.';

describe('when type a valid term', () => {
  it('update the state', () => {
    const { getByPlaceholderText } = render(<Search />;

    // this doesn't work. The handleOnChange method is private. How to deal with this?
    const handlerSpy = jest.spyOn(Search, 'handleOnChange');

    fireEvent.click(getByPlaceholderText(/search/i), { target: { value: 'moon' } });

    expect(handlerSpy).toHaveReturnedWith(true);
  });
});


我不知道我是否尝试了错误的方法。我只需要测试如果用户键入一个空术语会发生什么。感谢任何建议。

3 个答案:

答案 0 :(得分:0)

请,如果您有更好的答案,请留在这里。在寻找了不同的方法之后,我意识到了另一种测试方法。

首先,我将当前状态附加到搜索字段的value属性。

这样,我可以检查搜索字段的属性值是否符合要求。


import { render, fireEvent } from '@testing-library/react';
import Search from '.';

describe('when type a valid term', () => {
  it('update the state', () => {
    const { getByPlaceholderText } = render(<Search />;
    const inputField = getByPlaceholderText(/search/i);
    fireEvent.change(inputField, { target: { value: 'moon' } });

    expect(inputField).toHaveValue('moon');
  });
});

也可以编写快照测试。

答案 1 :(得分:0)

您当前的方法更接近于酶测试的方式(测试实施细节)。我建议检查测试库https://testing-library.com/docs/intro#the-problem

的文档

您应该像最终用户正在与您的应用进行交互一样进行测试。更合适的测试可能是:

describe('when no search is present', () => {
  it('displays placeholder text', () => {
    const { getByPlaceholderText } = render(<Search />;
    const searchInput = getByPlaceholderText(/search/i)
    expect(searchInput).toBeInTheDocument()
  });
});

以这种方式进行测试将为您提供所需的信心以及代码覆盖率

答案 2 :(得分:-2)

您很难测试它,因为您要同时测试很多东西。

这是一种解决方案,可以将您的状态与您的渲染脱钩。
我将您的组件转换为类组件,因此现在您可以使用enzyme访问状态和方法。

注意:您的示例非常简单,这些测试听起来有些过头。 但这是了解您要测试的什么以及可以用于每个测试用例的什么工具的好例子。

作为一般建议,当您难以测试某些东西时,通常是因为您正在测试太多东西
而且,如果您仅测试一件事,那么也许您在同一地方上有太多东西。

import React from "react";

export default class Search extends React.Component {

    constructor(props) {
        super(props);
        this.handleOnChange = this.handleOnChange.bind(this);
        this.state = {
            term: ''
        }
    }

    handleOnChange(value) {
        if (!value) {
            return false;
        }
        this.setState({term: value});
        return true;
    }

    render() {
        return <SearchInput handleOnChange={this.handleOnChange} />
    }
}

export function SearchInput(props) {
    return <input data-testid="search-input" type="text" placeholder="Search"
           onChange={e => props.handleOnChange(e.target.value)} />
}

现在我们可以独立测试每个功能了。

import React from "react";
import { render } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import {shallow} from "enzyme";
import Search, {SearchInput} from "./App";

describe('Search section', () => {
    describe("Search", () => {
        const searchWrapper = shallow(<Search />);

        it('should update the term on change', () => {
            searchWrapper.instance().handleOnChange("test-input");
            expect(searchWrapper.instance().state.term).toBe("test-input");
        });

        it("should render the search input with handleOnChange", () => {
            const searchInput = searchWrapper.find(SearchInput);
            expect(searchInput.props().handleOnChange).toBe(searchWrapper.instance().handleOnChange);
        });
    });

    describe("Search input", () => {
        it("should call handleOnChange callback when typing", () => {
            const onChangeMock = jest.fn();
            const searchInput = render(<SearchInput handleOnChange={onChangeMock} />);

            userEvent.type(searchInput.getByTestId('search-input'), 'test input');

            expect(onChangeMock).toBeCalledWith('test input');
        });
    });
});