функция настройки возвращает undefined при тестировании пользовательского хука use

У меня есть код для usePrevious из Интернета. Код для usePrevious выглядит так:

export const usePrevious = (value) => {
  const ref = useRef();
  useEffect(() => {
    ref.current = value;
  }, [value]);
  return ref.current;
};

Теперь я учусь тестировать реакцию с шуткой и энзимом. Итак, я попытался протестировать usePrevious и получил некоторые проблемы. Вот мой тестовый пример:

import React from 'react';
import { render } from 'enzyme';

import { usePrevious } from './customHooks';

const Component = ({ children, value }) => children(usePrevious(value));
const setup = (value) => {
  let returnVal = '';
  render(
    <Component value={value}>
      {
        (val) => {
          returnVal = val;
          return null;
        }
      }
    </Component>,
  );
  return returnVal;
};

describe('usePrevious', () => {
  it('returns something', () => {
    const test1 = setup('test');
    const test2 = setup(test1);
    expect(test2).toBe('test');
  });
});

Когда выполнение теста завершается, я получаю такую ​​ошибку:

Expected: 'test', Received: undefined

Может ли кто-нибудь сообщить мне, почему я получаю undefined и правильный ли это способ протестировать пользовательские хуки в ответ?

После предложения из комментариев от @Dmitrii G я изменил свой код, чтобы повторно визуализировать компонент (ранее я повторно монтировал компонент).

Вот обновленный код:

import React from 'react';
import PropTypes from 'prop-types';
import { shallow } from 'enzyme';

import { usePrevious } from './customHooks';

const Component = ({ value }) => {
  const hookResult = usePrevious(value);
  return (
    <div>
      <span>{hookResult}</span>
      <span>{value}</span>
    </div>
  );
};

Component.propTypes = {
  value: PropTypes.string,
};

Component.defaultProps = {
  value: '',
};


describe('usePrevious', () => {
  it('returns something', () => {
    const wrapper = shallow(<Component value="test" />);
    console.log('>>>>> first time', wrapper.find('div').childAt(1).text());
    expect(wrapper.find('div').childAt(0).text()).toBe('');

    // Test second render and effect
    wrapper.setProps({ value: 'test2' });
    console.log('>>>>> second time', wrapper.find('div').childAt(1).text());
    expect(wrapper.find('div').childAt(0).text()).toBe('test');
  });
});

Но все равно получаю ту же ошибку

Expected: "test", Received: ""

Тесты проходят, когда вводится время ожидания:

import React from 'react';
import PropTypes from 'prop-types';
import { shallow } from 'enzyme';

import { usePrevious } from './customHooks';

const Component = ({ value }) => {
  const hookResult = usePrevious(value);
  return <span>{hookResult}</span>;
};

Component.propTypes = {
  value: PropTypes.string,
};

Component.defaultProps = {
  value: '',
};


describe('usePrevious', () => {
  it('returns empty string when component is rendered first time', () => {
    const wrapper = shallow(<Component value="test" />);
    setTimeout(() => {
      expect(wrapper.find('span').text()).toBe('');
    }, 0);
  });
  it('returns previous value when component is re-rendered', () => {
    const wrapper = shallow(<Component value="test" />);
    wrapper.setProps({ value: 'test2' });
    setTimeout(() => {
      expect(wrapper.find('span').text()).toBe('test');
    }, 0);
  });
});

Я не большой поклонник использования settimeout, поэтому чувствую, что, вероятно, делаю какую-то ошибку. Если кто-то знает решение, которое не использует settimeout, не стесняйтесь публиковать здесь. Спасибо.


person Vishal    schedule 30.11.2019    source источник
comment
в чем проблема?   -  person Dmitrii G.    schedule 30.11.2019
comment
@DmitriiG. Я ожидаю, что мой тест вернет test, но он возвращает undefined   -  person Vishal    schedule 30.11.2019
comment
ваш usePrevious всегда возвращает «предыдущее» значение, которое в вашем случае изначально не определено. Ваш код работает так, как задумано.   -  person Dmitrii G.    schedule 30.11.2019
comment
@ DmitriiG.Я дважды вызываю функцию настройки, поэтому ожидаю значение test вместо undefined   -  person Vishal    schedule 30.11.2019
comment
@ DmitriiG, дайте мне знать, как это правильно проверить?   -  person Vishal    schedule 30.11.2019
comment
проверьте здесь responsejs.org/docs/hooks-faq.html, как тестировать компоненты с крючковой частью   -  person Dmitrii G.    schedule 30.11.2019
comment
хорошо, я проверю это и попытаюсь решить проблему. Спасибо.   -  person Vishal    schedule 30.11.2019
comment
Причина, по которой ваш не работает, заключается в том, что вы создаете два разных компонента с двумя разными usePrevious refs. в ссылке, которой я поделился, они показывают, как создать один компонент и сделать его повторную визуализацию, которая заполнит ваше usePrevious значением   -  person Dmitrii G.    schedule 30.11.2019
comment
@ DmitriiG: Я читал это и обнаружил, что они используют состояние. Они имитируют нажатие кнопки, которая изменяет состояние, и из-за этого их компонент перерисовывается. Должен ли я сделать то же самое, чтобы проверить свой крючок? Я не знаю, стоит ли это делать для тестирования кастомного хука? Должен признать, что я новичок в тестировании.   -  person Vishal    schedule 30.11.2019
comment
@ DmitriiG.Я попытался перерисовать компонент вместо того, чтобы монтировать его снова. По-прежнему получаю те же результаты. Пожалуйста, взгляните на рассматриваемую часть обновления.   -  person Vishal    schedule 30.11.2019
comment
@ DmitriiG.Я опубликовал решение вопроса. Но для этого используется settimeout. Не могли бы вы помочь мне улучшить тест?   -  person Vishal    schedule 30.11.2019
comment
попробуйте использовать mount(). У поверхностного рендерера есть известные проблемы с разрушением useEffect, я удивлен, что он работает с тайм-аутом в качестве временного решения.   -  person skyboyer    schedule 30.11.2019
comment
@skyboyer Я постараюсь это сделать и сообщу вам о результатах. Спасибо   -  person Vishal    schedule 30.11.2019
comment
@skyboyer Да, при использовании mount() вместо shallow() мне не нужно устанавливать тайм-аут. Спасибо за помощь. Пожалуйста, опубликуйте его как ответ, чтобы я мог закрыть вопрос, и кто-то в будущем, наткнувшись на это, может найти здесь решение.   -  person Vishal    schedule 30.11.2019


Ответы (1)


Enzyme под капотом использует неглубокий рендерер React. И у него есть проблема с запущенными эффектами. Не уверен, скоро ли это будет исправлено. Обходной путь с setTimeout был для меня неожиданностью, я не знал, что это работает. К сожалению, это не универсальный подход, так как вам нужно будет использовать его при любом изменении, которое требует повторной визуализации. Действительно хрупкий.

Вместо этого вы можете использовать mount().

Также вы можете имитировать неглубокий рендеринг с mount() с имитацией каждого вложенного компонента:

jest.mock("../MySomeComponent.jsx", () =>
  (props) => <span {...props}></span>
);
person skyboyer    schedule 30.11.2019
comment
Спасибо, что разместили это в качестве ответа, я думаю, что это может помочь кому-то в будущем. - person Vishal; 30.11.2019