Подтвердить что ты не робот

Тестирование изменений состояния реактивного компонента и шпионажа на методы экземпляра с использованием фермента

Я работаю над компонентом-оболочкой для плавной загрузки изображений в React. Я использую фермент с моккой, chai и sinon до unit test моего компонента. В тесте здесь я пытаюсь проверить, что:

  • Состояние компонента обновляется при загрузке изображения

  • метод экземпляра onLoad для компонента был вызван

const wrapper = shallow( );

const onLoad = wrapper.find('img').props().onLoad;
const onLoadSpy = sinon.spy(onLoad); wrapper.update();
const status = wrapper.state().status;
expect(onLoadSpy).to.have.been.called;
expect(status).to.equal('LOADED');

Я нахожу, что ни обновление состояния не отражается ферментом, либо количество вызовов для шпиона onLoad обновляется. Это соответствующий код для теста:

export default class Image extends Component {
  constructor(props) {
    super(props);
    if (props.src != null && typeof props.src === 'string') {
      this.state = {
        status: LOADING,
      };
    } else {
      this.state = {
        status: PENDING,
      };
    }
    this.onLoad = this.onLoad.bind(this);
  }

  onLoad() {
    this.setState({
      status: LOADED,
    });
  }

  render() {
    //lots of code above the part we care about
    const defaultImageStyle = style({
      opacity: 0,
      transisition: 'opacity 150ms ease',
    });

    const loadedImageStyle = style({
      opacity: 1,
    });

    let imageStyle = defaultImageStyle;

    if (this.state.status === LOADED) {
      imageStyle = merge(defaultImageStyle, loadedImageStyle);
    } else {
      imageStyle = defaultImageStyle;
    }


    let image;
    if (alt != null) {
      image = (<img
        className={imageStyle}
        src={src}
        width={width}
        height={height}
        alt={alt}
        onLoad={this.onLoad}
      />);
    } else {
      image = (<img
        className={imageStyle}
        src={src}
        width={width}
        height={height}
        role="presentation"
        onLoad={this.onLoad}
      />);
    }

    let statusIndicator = null;
    if (this.state.status === LOADING) {
      statusIndicator = (<div className={loadingStyle}></div>);
    }

    return (<div className={wrapperStyle}>
      {statusIndicator}
      {image}
    </div>);

    }}

Чтобы посмотреть полный код для лучшего контекста:

4b9b3361

Ответ 1

Это можно проверить, не полагаясь на sinon. Ожидая, что прослушиватели событий onLoad и onFire будут вызваны, тесты проверяют, запускают ли img события load и error.

Вместо этого события simulate img используют enzyme и проверяют, что происходит соответствующий переход состояния:

it('has a state of LOADED if a good src prop is supplied', () => {
  const wrapper = shallow(<Image 
    src="anyString.jpg"
    width={300}
    height={300}
  />);

  const img = wrapper.find('img');
  img.simulate('load');
  const status = wrapper.state().status;
  expect(status).to.equal('LOADED');
});

Это также устраняет необходимость в mount компоненте. Обновленные тесты можно найти здесь.

Ответ 2

Основная проблема, которую я вижу при таком подходе, состоит в том, что состояние - это внутренняя вещь, а не то, что следует знать за пределами компонента. Теперь вы пропускаете информацию о состоянии (в данном случае "статус") в тесты.

Это означает, что вы не проводите "тестирование черного ящика", которое является наиболее ценным типом тестов. Вы просочились детали реализации компонента. Другими словами, "Инкапсуляция" должна быть высоко оценена.

Возможно, есть лучшие способы проверить это. Например, вы также можете экспортировать презентационный компонент, который принимает части состояния, которые необходимо проверить, в качестве реквизита. Или найдите элемент, который будет отображаться, когда статус "ЗАГРУЗЕН" с помощью фермента метод поиска.