Ожидается (...). toHaveBeenCalled () завершается ошибкой, даже если вызывается метод компонента React - PullRequest
2 голосов
/ 18 марта 2019

Я пытаюсь проверить, вызывается ли метод компонента React с использованием энзима и шутки.Предполагается, что функция вызывается, когда элемент <section> становится не сфокусированным (при размытии).Компонент подключен к хранилищу Redux, но также экспортируется по имени, чтобы исключить Redux из уравнения.Ниже приведена упрощенная версия компонента:

export class Section extends React.Component {
  constructor(props) {
    super(props)
    this.formRef = React.createRef();
    this.submitForm = this.submitForm.bind(this);
    this.state = {
      formSubmitted: false
    }
  }

  submitForm() {
    console.log("form submitted");
    this.setState({ formSubmitted: true })
    if (this.formRef && this.formRef.current) {
      this.formRef.current.submit();
    }
  }

  render() {
    return (
      <section onBlur={this.submitForm}>
        <form ref={this.formRef} action={url} method='post'>
          <input type="text" name="something" />
        </form>
      </section>
    );
  }
}

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(Section);

Я пробовал все виды комбинаций для части spyOn, так как она, кажется, является корнем проблемы.Все не удалось.

import React from 'react';
import { shallow } from 'enzyme';
import { Section } from './Section';

describe('Section', () => {
  it('should submit form on blur', () => {
    const wrapper = shallow(<Section content={{ body: [] }} />);
    const spy = spyOn(wrapper.instance(), 'submitForm');
    const spy2 = spyOn(Section.prototype, 'submitForm');
    const spy3 = jest.spyOn(wrapper.instance(), 'submitForm');
    const spy4 = jest.spyOn(Section.prototype, 'submitForm');

    wrapper.find('section').simulate('blur');
    expect(wrapper.state().formSubmitted).toEqual(true);
    expect(spy).toHaveBeenCalled();
    expect(spy2).toHaveBeenCalled();
    expect(spy3).toHaveBeenCalled();
    expect(spy4).toHaveBeenCalled();
  })
})

Я дал компоненту состояние и проверил его в дополнение к тестированию expect(...).toHaveBeenCalled, чтобы проверить, была ли функция на самом деле вызвана, и, похоже, это так.

На консоли появляется console.log('form submitted').

Тест expect(wrapper.state().formSubmitted).toEqual(true); пройден, что указывает на то, что вызывается правильная функция.Тем не менее, я не хочу иметь ненужное состояние только для теста.Только что было добавлено состояние, чтобы утверждать, что вызывается метод submitForm.

Все утверждения expect(...).toHaveBeenCalled() завершаются с ошибкой Expected spy to have been called, but it was not called или Expected mock function to have been called, but it was not called.

Ответы [ 2 ]

3 голосов
/ 19 марта 2019

Интересно, что этот вопрос затрагивает некоторые более необычные аспекты JavaScript.


Что происходит

submitForm изначально определяется как метод-прототип :

class Section extends React.Component {
  ...
  submitForm() { ... }
  ...
}

... означает, что вы бы создали spy следующим образом:

jest.spyOn(Section.prototype, 'submitForm');

... но затем оно переопределяется как свойство экземпляра в constructor:

this.submitForm = this.submitForm.bind(this);

... означает, что вы бы сейчас создали spy следующим образом:

jest.spyOn(wrapper.instance(), 'submitForm');

... но это все еще не работает, потому что onBlur связывается напрямую до this.submitForm во время render:

<section onBlur={this.submitForm}>

Таким образом, способ, которым код в настоящее время написан, фактически делает невозможным шпионить за submitForm, потому что для создания spy требуется instance, но instance недоступен, пока компонент не рендерится и onBlur привязывается непосредственно к this.submitForm во время рендеринга.


Решение

Измените onBlur на функцию, которая вызывает this.submitForm, чтобы при каждом срабатывании onBlur она вызывала текущее значение из this.submitForm:

render() {
  return (
    <section onBlur={() => this.submitForm()}>
      <form ref={this.formRef} action={url} method='post'>
        <input type="text" name="something" />
      </form>
    </section>
  );
}

... затем, когда вы замените submitForm на spy на instance, spy будет вызван, когда onBlur выстрелит:

describe('Section', () => {
  it('should submit form on blur', () => {
    const wrapper = shallow(<Section content={{ body: [] }} />);
    const spy = jest.spyOn(wrapper.instance(), 'submitForm');

    wrapper.find('section').simulate('blur');
    expect(wrapper.state().formSubmitted).toEqual(true);
    expect(spy).toHaveBeenCalled();  // Success!
  })
})
1 голос
/ 02 апреля 2019

На самом деле вы хотите проверить, вызывается ли submit() формы, когда Section теряет фокус, верно? Таким образом, вам не нужно проверять, был ли вызван внутренний метод - он не будет гарантировать, отправлена ​​форма или нет. Также это может привести к ложноотрицательным результатам в случае рефакторинга (внутренний метод переименован, и все работает, но тест не пройден).

Вместо этого вы можете смоделировать объект ref. После этого вы сможете проверить, была ли отправлена ​​форма

it('should submit form on blur', () => {
  const wrapper = shallow(<Section content={{ body: [] }} />);
  wrapper.instance().formRef.current = { submit: jest.fn()};
  wrapper.find('section').simulate('blur');
  expect(wrapper.instance().formRef.current.submit).toHaveBeenCalled();
})

Конечно, тест также опирается на внутренние компоненты компонента (на свойство, содержащее ref). Но так я считаю, что тест более ... скажем, надежный.

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