Как проверить компоненты класса в реакции

9

Я пытаюсь провести модульное тестирование, я создал песочницу с поддельным примером https://codesandbox.io/s/wizardly-hooks-32w6l (на самом деле у меня есть форма)

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = { number: 0 };    
  }

  handleSubmit = (number1, number2) => {
    this.setState({ number: this.handleMultiply(number1, number2) })
  }

  handleMultiply = (number1, number2) => {
    return number1 * number2
  }

  render() {
    const { number } = this.state;

    return (
      <div className="App">
        <form onSubmit={e => this.handleSubmit(3, 7)}>       
          <input type="submit" name="Submit" value="Multiply" />
        </form>
        <Table number={number} />
      </div>
    );
  }
}

export default App;

Поэтому моей первоначальной идеей было попытаться проверить функцию умножения. И сделал это, что, очевидно, не работает

import App from "../src/App";

test("Multiply", function() {
  const expected = 21;
  const result = App.handleMultiply(3, 7);
  expect(result).toBe(expected);
});

я получил

_App.default.handleMultiply не является функцией

Правильный ли мой подход? Если да, то как мне проверить функции? Иначе, я должен проверять с точки зрения пользователя, а не для внутренних функций (это то, что я прочитал)? Должен ли я проверить вывод на экран (я не думаю, что это разумно)?

user3808307
источник
2
Вы подходите к этому с неправильным мышлением. Вместо этого инициируйте отправку формы, затем проверьте, чтобы убедиться, что состояние обновлено соответствующим образом, включая логику умножения.
Александр Старосельский
@AlexanderStaroselsky хорошо, спасибо, я постараюсь и сделаю более конкретный вопрос, если я
застряну
@AlexanderStaroselsky, что если форма в дочернем компоненте и обработчики отправки в родительском компоненте? Нужно ли проводить там интеграционные тесты?
user3808307
1
Это может быть вопросом мнения, но я бы определенно проверил это отдельно. Тесты для потомка будут состоять в том, что при отправке он запускает функцию, переданную от родителя через реквизиты, а затем проверяет, что состояние рендерится так, как вы ожидаете. Для родителя я бы запустил событие и убедился, что состояние обновлено правильно.
Александр Старосельский
@AlexanderStaroselsky Спасибо
пользователь3808307

Ответы:

4

Вы можете использовать метод instance ()enzyme для получения экземпляра компонента React. Затем вызовите handleMultiplyметод напрямую и сделайте для него утверждение. Кроме того, если у handleMultiplyметода есть побочный эффект или очень сложные вычисления, вам нужно сделать для него простое возвращаемое значение. Это создаст изолированную тестовую среду для handleSubmitметода. Это означает, что handleSubmitметод не будет зависеть от возвращаемого значения реальной реализации handleMultiplyметода.

Например

app.jsx:

import React from 'react';
import { Table } from './table';

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = { number: 0 };
  }

  handleSubmit = (number1, number2) => {
    this.setState({ number: this.handleMultiply(number1, number2) });
  };

  handleMultiply = (number1, number2) => {
    return number1 * number2;
  };

  render() {
    const { number } = this.state;

    return (
      <div className="App">
        <form onSubmit={(e) => this.handleSubmit(3, 7)}>
          <input type="submit" name="Submit" value="Multiply" />
        </form>
        <Table number={number} />
      </div>
    );
  }
}

export default App;

table.jsx:

import React from 'react';

export const Table = ({ number: num }) => {
  return <div>table: {num}</div>;
};

app.test.jsx:

import App from './app';
import { shallow } from 'enzyme';

describe('59796928', () => {
  let wrapper;
  beforeEach(() => {
    wrapper = shallow(<App></App>);
  });
  describe('#handleSubmit', () => {
    it('should pass', () => {
      expect(wrapper.exists()).toBeTruthy();
      wrapper.find('form').simulate('submit');
      expect(wrapper.state()).toEqual({ number: 21 });
    });
  });
  describe('#handleMultiply', () => {
    it('should pass', () => {
      const comp = wrapper.instance();
      const actual = comp.handleMultiply(2, 10);
      expect(actual).toBe(20);
    });
  });
});

Результаты модульных испытаний с отчетом о покрытии:

 PASS  src/stackoverflow/59796928/app.test.jsx (11.688s)
  59796928
    #handleSubmit
       should pass (16ms)
    #handleMultiply
       should pass (9ms)

-----------|----------|----------|----------|----------|-------------------|
File       |  % Stmts | % Branch |  % Funcs |  % Lines | Uncovered Line #s |
-----------|----------|----------|----------|----------|-------------------|
All files  |    90.48 |      100 |    85.71 |    94.44 |                   |
 app.jsx   |      100 |      100 |      100 |      100 |                   |
 table.jsx |       50 |      100 |        0 |    66.67 |                 4 |
-----------|----------|----------|----------|----------|-------------------|
Test Suites: 1 passed, 1 total
Tests:       2 passed, 2 total
Snapshots:   0 total
Time:        13.936s

Исходный код: https://github.com/mrdulin/jest-codelab/tree/master/src/stackoverflow/59796928

slideshowp2
источник
Что если форма была в дочернем компоненте? Как бы я вызвал handleSubmit в тесте, в отличие от отправки формы? Спасибо
user3808307