Open GuoYongfeng opened 7 years ago
作者:郭永峰,用友网络公共前端团队负责人 本次系列文章分为上中下三篇,《在 2017 年学习 React + Redux 的一些建议(上篇)》 和 《在 2017 年学习 React + Redux 的一些建议(中篇)》 可点击阅读。 原创文章,版权所有,转载请注明出处。
我们可以组合使用一些测试工具来帮助测试 JS 代码,一般使用 Mocha/Chai 或是 Karma/Jasmine 。而如果当你想测试 angular 的代码时,你会发现还有更多的测试工具。不过对于 React 应用的测试,比较推荐使用 Airbnb 团队出品的 anzyme 来进行组件的测试,以保住组件的稳定可靠,目前使用非常广泛;而另一种方式是使用 Facebook 的 jest 来进行测试。
JS
Mocha/Chai
Karma/Jasmine
angular
Facebook
可能很多同学都觉得应该选择以上的某一个测试库来进行测试工作,不过,你也可以将 anzyme 和 jest 结合起来一起使用。特别是在进行一些 snapshot 快照测试的时候,两种都是互补的,它们已经是 React 应用测试中大家公认的标准库了。
anzyme
jest
snapshot
React
sinon 也是个非常优秀的测试辅助工具,可以帮助我们在 spy、stub、mock 等测试阶段提供相应的工具辅助测试。如果对这几个概念不太清晰,可以看看这里。
spy、stub、mock
另外,在这里给你隆重的给你推荐一篇 A. Sharif 写的 Some Thoughts On Testing React/Redux Applications,满满的干货分享哦。
Enzyme 可以帮助我们实现组件的单元测试和集成测试。这里我们可以通过三种方式来渲染组件:
Enzyme
shallow()
mount()
render()
shallow() 只能用来渲染不包含 children 的组件,mount() 则能够渲染所有的子组件。所以单元测试的时候可以使用 shallow() ,mount() 则一般用于集成测试。集成测试往往是很容易被割裂的,因为他需要测试由一组或是多个组件树组合的场景,所以集成测试一般维护成本是比较高的。所以我们可以多做一些小巧的单元测试,少做一些重要的集成测试。
第三种测试的方式是使用 render() 方法,具有类似 mount()方法的功能,不过 mount() 能够访问到组件的生命周期方法,比如 componentDidUpdate等。
componentDidUpdate
正如这个 issue 中提出的 API differences between render and mount/shallow,可以总结出:
shallow
componentDidMount or componentDidUpdate
mount
render
否则的话你需要为此付出很高的维护成本。
确认每个组件是否都有执行过单元测试,确认每个 props 和 callbacks 都在集成测试的时候传递给了对应的子组件。
为了保证组件测试用例的小颗粒度和简单化,你需要熟悉一下 selectors,Enzyme 提供了丰富的 selector 去深入组件树。
selectors
另外,建议使用 sinon 来测试 callbacks 回调函数,不要在组件中测试业务逻辑,这真不是个好注意。而是应该将业务逻辑从组件中解耦并对其进行测试。
最后,Facebook 出品的 Jest 也能在初期帮助我们更加轻量的执行测试,你可以非常简单就设置好 snapshot test,这样当组件的输出改变的话测试用例会自动的报出失败的结果,并且能够获取到错误信息。
所有的人都可能会对你说:你应该按测试驱动的模式来进行开发。但是,几乎没几个人会这么,项目需求如山的积压,上线的脚本火急火燎,测试驱动?玩呢?!可能大部分小伙伴都是这样的心声。
不过,如果你能够清晰的在 React + Redux 的应用中使用对应的测试方案对每个部分都进行测试,你就能够非常轻松的实现 TDD。尽管你会发现 reducer 的测试和组件的测试是很不一样的,但其实每种类型(reducer、component、.... )的测试模式其实都是一样的。
就拿 reducer 的测试为例吧,一般是期望 reducer(state, action) === newState,其实这种方式和 (input) => output 的模式是一样的。如果你要测试 state 的不可变性的话,建议你可以使用 deep-freeze,可以看下以下示例代码:
reducer(state, action) === newState
(input) => output
import deepFreeze from 'deep-freeze' const initialState = { ... }; const action = { type: ..., payload: ... }; const expectedState = { ... }; deepFreeze(initialState); expect(reducer(initialState, action)).to.equal(expectedState);
如果你能够很清晰的知道如何测试应用中的每一个部分,那就最好采用 TDD。
React 虽然是个 library,但是它的生态圈非常的丰富,会有非常多的可扩展框架或类库可以加入使用,但是千万别太快的加入这些扩展方案。并且每次新加入一个模块的时候,要在团队里面确认每个人都是清楚了解的。特别是对于 Redux 本身的一些生态扩展,会有非常多的一些小模块,比如下面这些:
library
Redux
action creators
reducers
form
HOC
同时,关注一些大牛的最佳实践,并且建立你自己的最佳实践。不过得确保团队中其他小伙伴也能理解。定义清晰的命名规则和目录结构,并且在项目做一些升级的时候得把这些约定提前讨论清楚。
全文完结,感谢你的阅读,希望整个系列的文章对你今后的学习有所帮助。
如果你想系统学习 React + Redux 技术栈的所有内容,请点我前往
Airbnb 的测试工具是Enzyme,文章前面在名字上有点问题,不过非常感谢博主的总结
在 2017 年学习 React + Redux 的一些建议(下篇)
关于测试的一些学习建议
我们可以组合使用一些测试工具来帮助测试
JS
代码,一般使用Mocha/Chai
或是Karma/Jasmine
。而如果当你想测试angular
的代码时,你会发现还有更多的测试工具。不过对于 React 应用的测试,比较推荐使用 Airbnb 团队出品的 anzyme 来进行组件的测试,以保住组件的稳定可靠,目前使用非常广泛;而另一种方式是使用Facebook
的 jest 来进行测试。可能很多同学都觉得应该选择以上的某一个测试库来进行测试工作,不过,你也可以将
anzyme
和jest
结合起来一起使用。特别是在进行一些snapshot
快照测试的时候,两种都是互补的,它们已经是React
应用测试中大家公认的标准库了。sinon 也是个非常优秀的测试辅助工具,可以帮助我们在
spy、stub、mock
等测试阶段提供相应的工具辅助测试。如果对这几个概念不太清晰,可以看看这里。多一些组件的单元测试,少一些集成测试
Enzyme
可以帮助我们实现组件的单元测试和集成测试。这里我们可以通过三种方式来渲染组件:shallow()
mount()
render()
shallow()
只能用来渲染不包含 children 的组件,mount()
则能够渲染所有的子组件。所以单元测试的时候可以使用shallow()
,mount()
则一般用于集成测试。集成测试往往是很容易被割裂的,因为他需要测试由一组或是多个组件树组合的场景,所以集成测试一般维护成本是比较高的。所以我们可以多做一些小巧的单元测试,少做一些重要的集成测试。第三种测试的方式是使用
render()
方法,具有类似mount()
方法的功能,不过mount()
能够访问到组件的生命周期方法,比如componentDidUpdate
等。正如这个 issue 中提出的 API differences between render and mount/shallow,可以总结出:
shallow
开始测试用例componentDidMount or componentDidUpdate
等方法也需要测试,那么使用mount
方法吧mount
方法吧render
方法吧保证测试用例简单、最小颗粒度
否则的话你需要为此付出很高的维护成本。
确认每个组件是否都有执行过单元测试,确认每个 props 和 callbacks 都在集成测试的时候传递给了对应的子组件。
为了保证组件测试用例的小颗粒度和简单化,你需要熟悉一下
selectors
,Enzyme
提供了丰富的 selector 去深入组件树。另外,建议使用 sinon 来测试 callbacks 回调函数,不要在组件中测试业务逻辑,这真不是个好注意。而是应该将业务逻辑从组件中解耦并对其进行测试。
最后,Facebook 出品的 Jest 也能在初期帮助我们更加轻量的执行测试,你可以非常简单就设置好 snapshot test,这样当组件的输出改变的话测试用例会自动的报出失败的结果,并且能够获取到错误信息。
拥抱 TDD(测试驱动开发)
所有的人都可能会对你说:你应该按测试驱动的模式来进行开发。但是,几乎没几个人会这么,项目需求如山的积压,上线的脚本火急火燎,测试驱动?玩呢?!可能大部分小伙伴都是这样的心声。
不过,如果你能够清晰的在 React + Redux 的应用中使用对应的测试方案对每个部分都进行测试,你就能够非常轻松的实现 TDD。尽管你会发现 reducer 的测试和组件的测试是很不一样的,但其实每种类型(reducer、component、.... )的测试模式其实都是一样的。
就拿 reducer 的测试为例吧,一般是期望
reducer(state, action) === newState
,其实这种方式和(input) => output
的模式是一样的。如果你要测试 state 的不可变性的话,建议你可以使用 deep-freeze,可以看下以下示例代码:多组件测试
关于资源加载的选择
React
虽然是个library
,但是它的生态圈非常的丰富,会有非常多的可扩展框架或类库可以加入使用,但是千万别太快的加入这些扩展方案。并且每次新加入一个模块的时候,要在团队里面确认每个人都是清楚了解的。特别是对于Redux
本身的一些生态扩展,会有非常多的一些小模块,比如下面这些:action creators
和reducers
之前,就不要添加 redux-actionsform
表单和表单验证的时候,就不要加入 redux-formselectors
之前,就不要加入 reselectHOC
之前,就不要加入 recompose同时,关注一些大牛的最佳实践,并且建立你自己的最佳实践。不过得确保团队中其他小伙伴也能理解。定义清晰的命名规则和目录结构,并且在项目做一些升级的时候得把这些约定提前讨论清楚。
保持持续的技术学习热情
结束语
全文完结,感谢你的阅读,希望整个系列的文章对你今后的学习有所帮助。
如果你想系统学习 React + Redux 技术栈的所有内容,请点我前往