简体   繁体   English

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

[英]How to spy on method inside a React Functional Component?

I have this functional component.我有这个功能组件。

Search.js搜索.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搜索测试.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);
  });
});


I don't know if I'm trying the wrong approach.我不知道我是否在尝试错误的方法。 I just need to test what happens if the user type an empty term.我只需要测试如果用户输入一个空术语会发生什么。 Appreciate any suggestion.感谢任何建议。

Please, if you have a better answer left it here.请,如果您有更好的答案,请将其留在这里。 After a search for different approaches, I realized another way to test it.在搜索了不同的方法后,我发现了另一种测试方法。

First, I attached the current state to the value attribute of my search field.首先,我将当前状态附加到我的搜索字段的 value 属性。

This way, I can check if the attribute value of my search field changes accordling.这样,我可以检查我的搜索字段的属性值是否发生了相应的变化。


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

It's possible to write a snapshot test as well.也可以编写快照测试。

Your current approach is closer to how tests were done in enzyme (test implementation details).您当前的方法更接近于在酶中进行测试的方式(测试实施细节)。 I'd recommend checking the documentation of testing-library https://testing-library.com/docs/intro#the-problem我建议检查 testing-library https://testing-library.com/docs/intro#the-problem的文档

You should be testing as if your final user was interacting with your app.您应该像最终用户与您的应用程序交互一样进行测试。 A more appropriate test might be:更合适的测试可能是:

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

testing this way will give you the confidence you need and also code coverage以这种方式进行测试将为您提供所需的信心以及代码覆盖率

You struggle to test it because you're testing to many things at the same time.你很难测试它,因为你同时测试了很多东西。

Here's a solution where I decouple your state from your rendering.这是我将您的状态与渲染分离的解决方案。
I converted your component to a class component so now you can have access to state and methods using enzyme .我将您的组件转换为类组件,因此现在您可以使用Enzyme访问状态和方法。

Note: your example is very simple and these tests might sound overkill.注意:您的示例非常简单,这些测试听起来可能有点过分。 But it's a nice example to understand what you are testing and what tool you can use for each test case.但它是一个很好的例子来理解正在测试,什么工具,你可以使用每个测试案例。

As a general advice, when you struggle to test something, it's usually because you're testing too many things .作为一般建议,当您难以测试某些内容时,通常是因为您测试了太多内容
And if you're only testing one thing, then maybe you have too many thing at the same place .如果你只测试一件事,那么可能你在同一个地方有太多的东西。

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

Now we can test each feature independently.现在我们可以独立测试每个功能。

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM