Как шпионить за методом внутри функционального компонента React? - PullRequest
0 голосов
/ 21 февраля 2020

У меня есть этот функциональный компонент.

Поиск. 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 ]

1 голос
/ 22 февраля 2020

Ваш текущий подход ближе к тому, как тесты проводились в ферменте (подробности реализации теста). Я бы порекомендовал проверить документацию библиотеки тестирования 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()
  });
});

, тестирование таким способом даст вам необходимую уверенность, а также покрытие кода

0 голосов
/ 22 февраля 2020

Вы изо всех сил пытаетесь его протестировать, потому что вы тестируете одновременно много вещей.

Вот решение, в котором я отделил ваше состояние от вашего рендеринга.
Я преобразовал ваш компонент в компонент класса так что теперь вы можете иметь доступ к состоянию и методам, используя фермент .

Примечание: ваш пример очень прост, и эти тесты могут показаться излишними. Но это хороший пример, чтобы понять, что вы тестируете, и какой инструмент, который вы можете использовать для каждого теста.

В качестве общего совета, когда вы пытаетесь протестировать что-то, обычно потому, что вы тестируете слишком много вещей .
И если вы тестируете только одну вещь, то, возможно, у вас слишком много вещей в том же месте .

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');
        });
    });
});
0 голосов
/ 22 февраля 2020

Пожалуйста, если у вас есть лучший ответ, оставьте его здесь. После поиска различных подходов я нашел другой способ проверить его.

Сначала я прикрепил текущее состояние к атрибуту значения моего поля поиска.

Таким образом, я могу проверить, значение атрибута моего поля поиска изменяется соответствующим образом.


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

Можно также написать тест снимка.

...