Cómo probar componentes de clase en reaccionar

9

Estoy probando algunas pruebas unitarias, creé un sandbox con un ejemplo falso https://codesandbox.io/s/wizardly-hooks-32w6l (en realidad tengo un formulario)

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;

Entonces, mi idea inicial era tratar de probar la función de multiplicación. E hice esto, que obviamente no funciona

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

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

yo obtengo

_App.default.handleMultiply no es una función

¿Es correcto mi enfoque? En caso afirmativo, ¿cómo pruebo las funciones? De lo contrario, ¿debería probar desde el punto de vista del usuario en lugar de las funciones internas (esto es lo que leo)? ¿Debo probar la salida en la pantalla (no creo que esto sea razonable)?

usuario3808307
fuente
2
Te estás acercando a esto con la mentalidad equivocada. En su lugar, active el envío del formulario y luego verifique que el estado se haya actualizado correctamente, incluida la lógica de multiplicación.
Alexander Staroselsky
@AlexanderStaroselsky ok, gracias, lo intentaré y haré una pregunta más específica si me
atoro
@AlexanderStaroselsky ¿y si el formulario en un componente secundario y los controladores de envío en el principal? ¿Necesito hacer pruebas de integración allí?
user3808307
1
Puede ser una cuestión de opinión, pero definitivamente probaría esto por separado. Las pruebas para el niño serían que al enviarlo desencadena la función pasada del padre a través de accesorios y luego también para probar que el estado se presenta como era de esperar. Para el padre, desencadenaría el evento y me aseguraría de que el estado se haya actualizado correctamente.
Alexander Staroselsky
@AlexanderStaroselsky Gracias
usuario3808307

Respuestas:

4

Puede usar el método instance () de enzymepara obtener la instancia de React Component. Luego, llame al handleMultiplymétodo directamente y haga la afirmación para ello. Además, si el handleMultiplymétodo tiene un efecto secundario o cálculos muy complicados, debe realizar un simple valor de retorno simulado para él. Hará un entorno de prueba aislado para el handleSubmitmétodo. Esto significa que el handleSubmitmétodo no dependerá del valor de retorno de la implementación real del handleMultiplymétodo.

P.ej

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 de la prueba unitaria con informe 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 fuente: https://github.com/mrdulin/jest-codelab/tree/master/src/stackoverflow/59796928

slideshowp2
fuente
¿Qué pasa si el formulario estaba en un componente secundario? ¿Cómo activaría handleSubmit en la prueba, aparte de enviar un formulario? Gracias
user3808307