[译] 更可靠的 React 组件:从"可测试的"到"测试通过的"

1,378 阅读3分钟

原文摘自:https://dmitripavlutin.com/7-architectural-attributes-of-a-reliable-react-component/#6testableandtested

一个被验证过针对给定的输入会渲染出符合期望的输出的组件,称为 测试过的(tested) 组件;

一个 可测试的(testable) 组件意味着其易于测试

如何确保一个组件如期望的工作呢?你可以说:“我都自己手动试过的呀。”

如果你打算对每个组件的每个改动都手动验证的话,或早或晚的,你就会跳过这项苦差事了,而小的瑕疵早晚也会出现。

这就是对组件的自动化验证,也就是单元测试(unit test),为何重要的原因。单元测试保证了每次对组件做出的更改后,组件都能正确工作。

单元测试并不只与早期发现 bug 有关。另一个重要的方面是用其检验组件架构化水平优劣的能力。

我觉得这句话格外的重要:

一个 无法测试难以测试 的组件,基本上就等同于 设计得很拙劣 的组件.

组件之所以难以测试时因为其有太多的 props、依赖、引用的模型和对全局变量的访问 -- 这都是不良设计的标志。

一个架构设计羸弱的组件,就会变成无法测试的,进而你就会简单的跳过单元测试,又导致了其保持未测试状态,这是一个恶性循环。

总之,许多应用为何是未测试状态的原因就是不良的组件设计。即便你想动手测试,也无处下手。

案例学习:可测试就意味着设计良好i

之前的文章中提及过 <Controls> 组件。

下面的代码测试了高度依赖于父组件结构的 <Controls> 版本:

import assert from 'assert';  
import { shallow } from 'enzyme';

class Controls extends Component {  
  render() {
    return (
      <div className="controls">
        <button onClick={() => this.updateNumber(+1)}>
          Increase
        </button> 
        <button onClick={() => this.updateNumber(-1)}>
          Decrease
        </button>
      </div>
    );
  }
  updateNumber(toAdd) {
    this.props.parent.setState(prevState => ({
      number: prevState.number + toAdd       
    }));
  }
}

class Temp extends Component {  
  constructor(props) {
    super(props);
    this.state = { number: 0 };
  }
  render() {
    return null;
  }
}

describe('<Controls />', function() {  
  it('should update parent state', function() {
    const parent = shallow(<Temp/>);
    const wrapper = shallow(<Controls parent={parent} />);

    assert(parent.state('number') === 0);

    wrapper.find('button').at(0).simulate('click');
    assert(parent.state('number') === 1);

    wrapper.find('button').at(1).simulate('click');
    assert(parent.state('number') === 0); 
  });
});

<Controls> 测试起来非常复杂,因为它关联了父组件的实现细节。

测试场景中需要一个额外的 <Temp> 组件,用来模拟父组件,检验 <Controls> 是否正确修改了父组件的状态。

<Controls> 独立于父组件的细节时,测试就简单了。让我们测试一下合理封装版本的 <Controls> 组件:

import assert from 'assert';  
import { shallow } from 'enzyme';  
import { spy } from 'sinon';

function Controls({ onIncrease, onDecrease }) {  
  return (
    <div className="controls">
      <button onClick={onIncrease}>Increase</button> 
      <button onClick={onDecrease}>Decrease</button>
    </div>
  );
}

describe('<Controls />', function() {  
  it('should execute callback on buttons click', function() {
    const increase = sinon.spy();
    const descrease = sinon.spy();
    const wrapper = shallow(
      <Controls onIncrease={increase} onDecrease={descrease} />
    );

    wrapper.find('button').at(0).simulate('click');
    assert(increase.calledOnce);
    wrapper.find('button').at(1).simulate('click');
    assert(descrease.calledOnce);
  });
});

封装好则测试易,反之不恰当的封装让测试变得困难。

可测试性是一个检验组件结构良好程度的实践标准。


(end)


----------------------------------------

转载请注明出处


长按二维码或搜索 fewelife 关注我们哦