在React / Redux中进行测试-如何保证状态已更新?

时间:2018-08-15 02:58:41

标签: javascript reactjs asynchronous redux

我正在为新的React应用编写测试,我对该项目的部分意图是完全了解该测试内容-它已经存在了一段时间,但我从未将其投入生产。

到目前为止,已经使用快照以及其他静态和同步方法编写了大量测试。直到现在我正在处理setState-> Expect(postFunctionState).toEqual(desiredState)情况,并且当我在控制台中记录日志流的方式并可以看到setState()正在被调用时,这似乎一直有效可以在浏览器中看到结果,我似乎无法编写可复制行为的测试。

以下是相关代码:

//Component (extracted):
export class CorsetCreator extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      productName: '',
      productType: 'Overbust',
      enabled: false,
      created: false,
    };
    this.handleSubmit = this.handleSubmit.bind(this);
    this.handleNameChange = this.handleNameChange.bind(this);
    this.handleProductChange = this.handleProductChange.bind(this);
  }

  handleNameChange(e) {
    this.setState({ productName: e.target.value });
    this.handleChange.bind(this)(e);
  }

  handleProductChange(e) {
    this.setState({ productType: e.target.value });
    this.handleChange.bind(this)(e);
  }

  handleChange(e) {
    e.preventDefault();

    if (e.target.value === '') {
      this.setState({ enabled: false }); //User should not be able to submit an empty product name
      return;
    }

    const { corsets } = this.props.corsetGallery;
    if (!corsets) {
      this.forceUpdate();
      this.setState({ enabled: true }); //Point of this exercise is to guarantee unique combos of name&type.  If there are no pre-existing corsets then no need to test for uniqueness
      return;
    }

    const productType =
      e.target.value === 'Underbust' || e.target.value === 'Overbust'
        ? e.target.value
        : this.state.productType;
    const productName =
      e.target.value === 'Underbust' || e.target.value === 'Overbust'
        ? this.state
        : e.target.value;

    const filteredCorsets = corsets.filter(
      corset => corset.type === productType && corset.name === productName,
    );

    this.setState({
      enabled: !(filteredCorsets && filteredCorsets.length > 0),
    });
  }

//Test (extracted)
  it('handles statechanges correctly with a valid new corset', () => {
    const store = configureStore({}, browserHistory);

    const creator = mount(
      <Provider store={store}>
        <CorsetCreator />
      </Provider>,
    );

    const namebox = creator.find('NameBox').at(0);
    const nameBoxField = namebox.find('input').at(0);
    const submitbutton = creator.find('SubmitButton').at(0);

    creator.setState({ enabled: false });
    expect(submitbutton.props().enabled).toEqual(false);

    nameBoxField.simulate('change', { target: { value: 'Test' } });
    creator.update();
    expect(creator.state().enabled).toEqual(true);
  });

因为setState是异步的,所以我觉得某种回调或promise可能是这里的解决方案,但我已经尝试过这两种方法,并且似乎无法通过最佳方式进行排序。考虑这种情况的最佳方法是什么?

1 个答案:

答案 0 :(得分:1)

TL; DR:记住React组件是函数。在他们所有的荣耀中,他们接受道具,并且您会收到render()函数的输出。测试输出。

如果状态中有变量,则很可能会将其传递给子组件或操纵当前组件的可视输出。或说状态中的项目将毫无用处:)

测试状态是多余的,就像测试React本身一样。

通常会引起关注的一个问题是“但是我正在通过使用setState(...)显示/隐藏该元素”,或者“我正在将状态作为道具传递给一个孩子”。

编写测试时,渲染组件。模拟动作并检查render函数的输出是否已更改。

以该组件为例:

class TextWithClick extends React.Component {
  state={ count: 0 };

  handleClick = () => {
    this.setState({ count: this.state.count + 1})
  }

  render() {
    return (
      <input
        value={this.state.count} {/* state passed down as props */}
        onClick={this.handleClick}
        />
    )
  }
}

ReactDOM.render(<TextWithClick/>
  , document.getElementById('root'))

很简单。单击输入字段,增加它显示的文本,这是一个道具。以下是一些测试断言:

// using enzyme
it("should increase the count", () => {
  const wrapper = shallow(<TextWithClick />);
  const input = wrapper.find("input").at(0);

  // test props
  input.simulate("click");
  expect(input.props().value).toEqual(1);

  input.simulate("click");
  expect(input.props().value).toEqual(2);

  input.simulate("click");
  expect(input.state().count).toEqual(3); // this tests React more than your component logic :)
});

记住React组件是函数。在他们所有的荣耀中,他们接受道具,并且您会收到render()函数的输出。测试输出。

对于Redux,也是一样。测试状态更改就像测试Redux的connect()功能一样。 Mozilla使用real redux store to test their app。即测试最终输出。

我从上面的链接中引用了有关测试React / Redux应用程序的信息(似乎无法在SO中多行显示blockquotes:

  • “我们调度真正的Redux动作来测试应用程序状态更改。我们仅使用浅层渲染测试每个组件一次。
  • “我们尽可能抵制完整的DOM渲染(使用mount())。
  • “我们通过检查属性来测试组件集成。
  • “静态类型有助于验证我们的组件属性。
  • “我们模​​拟用户事件并断言派遣了什么行动。

一篇好文章:Anthony Ng的Testing React Component’s State