如何在我的 class 组件中测试方法?

How do I test method inside my class component?

我有这个方法:

fizzBuzz = () => {
    let tokens = [];
    for (let i=1; i <= this.state.upperLimit; i++) {
        if (i % 15 === 0) {
            tokens.push("fizzbuzz");
        }  else if (i % 3 === 0) {
            tokens.push("fizz");
        } else if (i % 5 === 0) {
            tokens.push("buzz");
        } else {
            tokens.push(i) ;
        }    
    } 
    this.setState({message: tokens.join(" ")});
}

这只是 FizzBu​​zz 计算,让用户在输入字段中输入 fizzbuzz 实施的上限并提交。然后上面的方法进行一些计算并在屏幕上显示消息。

渲染内部的片段-return:

<input value={this.upperLimit} type="number" min="1" placeholder="Enter Integer"/>
<button type="button" onClick={this.fizzBuzz}>Submit</button>
<label>{this.state.message}</label>

我正在使用 Jest 和 React-Testing-Library,但我不确定如何测试此功能。我是否需要重新安排我的 class 组件内部的完成方式以使其可访问以供测试?

RTL suggests to test like a real user would,所以你应该根据显示的组件编写测试:

  • 更改输入值
  • 点击按钮
  • 测试标签中是否存在请求的消息
  • 使用不同的输入编写其他类似测试以涵盖所有情况。

所以好消息是您不需要对您的组件进行任何更改,您的单元测试应该与内部实现分离。 The implementation may change, without having to rewrite all your tests.

有关此主题的详细说明,请参阅 this issue

你的测试可能是这样的:

describe('fizzbuzz', () => {
  let rtl: RenderResult;

  beforeEach(() => {
    rtl = render(<Fizzbuzz />);
  });

  test('prints correct tokens for 16', () => {
    // I assume that the input has a label which is "label for input"
    // I order to test like a real user you should grab DOM elements with visual properties/texts
    // and avoid things like CSS selectors. data-testid attribute should be the last option to use
    fireEvent.change(rtl.getByLabelText('label for input'), { target: { value: '16' } });

    // ...but you can still use querySelector/querySelectorAll
    fireEvent.click(rtl.container.querySelector('button'));

    // check that what should be returned by the function is present in the document
    expect(rtl.getByText('0 1 2 fizz 4 buzz fizz 7 8 fizz 10 11 fizz 13 14 fizzbuzz 16')).toBeTruthy();
  });

  test('prints correct tokens for various other cases', () => {
    fireEvent.change(rtl.getByLabelText('label for input'), { target: { value: '4' } });
    fireEvent.click(rtl.container.querySelector('button'));
    expect(rtl.getByText('0 1 2 fizz 4')).toBeTruthy();

    fireEvent.change(rtl.getByLabelText('label for input'), { target: { value: '7' } });
    fireEvent.click(rtl.container.querySelector('button'));
    expect(rtl.getByText('0 1 2 fizz 4 buzz fizz 7')).toBeTruthy();

    fireEvent.change(rtl.getByLabelText('label for input'), { target: { value: '0' } });
    fireEvent.click(rtl.container.querySelector('button'));
    expect(rtl.getByText('0')).toBeTruthy();
  });

  test('behaves correctly with negative input', () => {
    fireEvent.change(rtl.getByLabelText('label for input'), { target: { value: '-7' } });
    fireEvent.click(rtl.container.querySelector('button'));
    expect(rtl.container.querySelector('label').textContent).toEqual('');
  });

  // add more if you feel that some cases are not covered
  // BTW the above tests should give you a 100% test coverage
  // This should not be a goal (you can get 100% test coverage passing for wrong code),
  // but it is still a good indicator.
  // https://jestjs.io/docs/en/cli.html#--coverageboolean
});