Jak testować komponenty klasy w reakcji

9

Próbuję niektórych testów jednostkowych, utworzyłem piaskownicę z fałszywym przykładem https://codesandbox.io/s/wizardly-hooks-32w6l (w rzeczywistości mam formularz)

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;

Więc moim początkowym pomysłem była próba przetestowania funkcji mnożenia. I zrobił to, co oczywiście nie działa

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

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

dostaję

_App.default.handleMultiply nie jest funkcją

Czy moje podejście jest właściwe? Jeśli tak, to jak mam przetestować funkcje? W przeciwnym razie powinienem testować z punktu widzenia użytkownika zamiast funkcji wewnętrznych (to właśnie czytam)? Czy powinienem testować dane wyjściowe na ekranie (nie sądzę, aby było to uzasadnione)?

użytkownik3808307
źródło
2
Podchodzisz do tego ze złym nastawieniem. Zamiast tego uruchom formularz przesłania, a następnie sprawdź, aby upewnić się, że stan został odpowiednio zaktualizowany, w tym logikę mnożenia.
Alexander Staroselsky
@AlexanderStaroselsky ok, dziękuję, spróbuję i
zadam
@AlexanderStaroselsky co zrobić, jeśli formularz w komponencie potomnym i prześlij procedury obsługi w rodzicu? Czy muszę tam przeprowadzać testy integracyjne?
user3808307
1
To może być kwestia opinii, ale zdecydowanie przetestowałbym je osobno. Testy dla dziecka polegałyby na tym, że po przesłaniu uruchamia funkcję przekazaną od rodzica za pośrednictwem rekwizytów, a następnie sprawdza, czy stan jest renderowany zgodnie z oczekiwaniami. Dla rodzica wyzwalam zdarzenie i upewniam się, że stan został poprawnie zaktualizowany.
Alexander Staroselsky
@AlexanderStaroselsky Dziękuję
użytkownik3808307

Odpowiedzi:

4

Możesz użyć metody instance () , enzymeaby uzyskać instancję React Component. Następnie wywołaj handleMultiplymetodę bezpośrednio i stwórz dla niej potwierdzenie. Ponadto, jeśli handleMultiplymetoda ma efekt uboczny lub bardzo skomplikowane obliczenia, musisz wykonać dla niej prostą próbkę zwracanej wartości. Stworzy izolowane środowisko testowe dla handleSubmitmetody. Oznacza to, że handleSubmitmetoda nie będzie zależała od wartości zwracanej rzeczywistej implementacji handleMultiplymetody.

Na przykład

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

Wyniki testu jednostkowego z raportem pokrycia:

 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

Kod źródłowy: https://github.com/mrdulin/jest-codelab/tree/master/src/stackoverflow/59796928

pokaz slajdówp2
źródło
Co jeśli formularz był w elemencie potomnym? W jaki sposób uruchomić wyzwalacz handleSubmit w teście, inny niż przy przesłaniu formularza? Dziękuję
użytkownik3808307,