如何在React中测试类组件


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不是函数

我的方法正确吗?如果是,那么如何测试功能?否则,我应该从用户的角度进行测试,而不是针对内部功能进行测试(这是我读到的内容)吗?我应该在屏幕上测试输出(我认为这不合理)吗?


2
您正以错误的思维方式来处理这个问题。而是触发表单提交,然后检查以确保状态已正确更新,包括乘法逻辑。
Alexander Staroselsky

@AlexanderStaroselsky好的,谢谢,我会尝试,并做一个更具体的问题,如果我被卡住了
user3808307

@AlexanderStaroselsky如果子组件中的表单和父组件中的提交处理程序怎么办?我需要在那里进行集成测试吗?
user3808307

1
可能是个见解,但我一定会分别测试它们。对孩子的测试将是在提交时触发它通过道具从父级传递的函数,然后还要测试状态是否按您期望的那样呈现。对于父级,我将触发事件并确保状态已正确更新。
Alexander Staroselsky

@AlexanderStaroselsky谢谢
user3808307

Answers:


4

您可以使用的instance()方法enzyme获取React Component的实例。然后,handleMultiply直接调用method并为其声明。此外,如果该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


如果表单在子组件中怎么办?我如何在测试中触发handleSubmit,以及如何使用表单提交?谢谢
user3808307
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.