Como testar componentes de classe em reagir

9

Estou tentando alguns testes de unidade, criei uma sandbox com um exemplo falso https://codesandbox.io/s/wizardly-hooks-32w6l (na realidade, tenho um formulário)

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;

Então, minha ideia inicial foi tentar testar a função multiplicar. E fez isso, o que obviamente não funciona

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

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

eu recebo

_App.default.handleMultiply não é uma função

Minha abordagem está certa? Se sim, como testo as funções? Senão, devo testar do ponto de vista do usuário em vez de funções internas (é isso que eu leio)? Devo testar a saída na tela (não acho que isso seja razoável)?

user3808307
fonte
2
Você está abordando isso com a mentalidade errada. Em vez disso, ative o envio do formulário e verifique se o estado foi atualizado adequadamente, incluindo a lógica de multiplicação.
Alexander Staroselsky 18/01
@AlexanderStaroselsky ok, obrigado, vou tentar e faço uma pergunta mais específica se eu ficar preso
user3808307 18/01
@AlexanderStaroselsky, e se o formulário em um componente filho e os manipuladores de envio no pai? Preciso fazer testes de integração lá?
user3808307 18/01
11
Pode ser uma questão de opinião, mas eu definitivamente os testaria separadamente. Os testes para a criança seriam que, ao enviar, ela dispara a função passada pelo pai via adereços e também para testar se o estado é renderizado conforme o esperado. Para o pai, eu acionaria o evento e garantiria que o estado fosse atualizado corretamente.
Alexander Staroselsky 18/01
@AlexanderStaroselsky Thank you
user3808307 19/01

Respostas:

4

Você pode usar o método instance ()enzyme para obter a instância do React Component. Em seguida, chame o handleMultiplymétodo diretamente e faça a afirmação para ele. Além disso, se o handleMultiplymétodo tiver um efeito colateral ou cálculos muito complicados, você precisará fazer um simples valor de retorno simulado. Isso criará um ambiente de teste isolado para o handleSubmitmétodo. Isso significa que o handleSubmitmétodo não dependerá do valor de retorno da implementação real do handleMultiplymétodo.

Por exemplo

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

Resultados do teste de unidade com relatório de cobertura:

 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

Código fonte: https://github.com/mrdulin/jest-codelab/tree/master/src/stackoverflow/59796928

slideshowp2
fonte
E se o formulário estivesse em um componente filho? Como eu acionaria o handleSubmit no teste, além do envio de um formulário? Obrigado
user3808307 20/01