对于使用 react 全家桶( react+redux-saga)作为技术栈的前端项目,大家是怎么进行单元测试和以测试作为驱动的开发的?

2017-04-29 01:00:57 +08:00
 ly710

最近我司在大力推广单元测试和测试驱动的开发方式(TDD),小弟做前端才半年多一点并且之前写后端代码的时候也没有接触过单元测试,所以没什么头绪,特来请教一下大家。

我们现在的前端项目用的是阿里的 dva 框架,dva 主要是对 redux,redux-saga,react-router 等库做了封装。现在代码的分层主要是 react + model,react 当作是纯模版用,所有组件都是 no state 的。而所有的 state 和业务逻辑都放在 model 里面( redux + redux-saga )。

在针对项目做单元测试的时候有以下几个疑问:

1.如何测试界面?

是否要针对样式和动效写单元测试?如果样式也写的话感觉工作量好大。还有就是如何测试一个组件是否 dispatch 了正确的 action。

2.什么样的写法才是 model 和 view 分离的很彻底的?

既然代码上做了 model 和 view 和分层就是为了分离业务逻辑和界面,react 组件只负责分发 action。但是组件分发什么样的 action 带了什么 payload,感觉本身也是业务逻辑的一部分。一些负复杂的操作我也可以 dispatch 一系列的 action 来完成,这样主要的业务逻辑其实还是写在模版里的。怎么写逻辑和显示分离更彻底的代码?以及判定是否合格的标准是什么?

3.如何测 sagas ?

针对每个 saga,比如带有异步获取数据逻辑的,是只要测到在 saga 里触发了存数据的 action 就算过,还是一定要确定 redux 的 store 里确实更新了给定的数据?在开发阶段就要覆盖每个代码分支正确以及错误的情况,还是说发生了 bug 之后再补上相应的测试用例?

下面是一段获取机械列表页的代码,不知道有没有大神科普一下如何写完善的单元测试?代码逻辑是先把新的查询参数存到 state 里,然后再从 state 里取参数去获取机械信息。

fetchMachines({ payload }, { call, put, select })  {
        if(payload) {
            const {
                state,
                oil_percent_max,
                oil_percent_min,
                order_by,
                order_type,
                page,
                ...params,
            } = payload;

            if(state !== undefined) {
                yield put({ type: 'saveState', payload: state });
            }

            if(oil_percent_max !== undefined) {
                yield put({ type: 'saveOilPercentMax', payload: oil_percent_max });
            }

            if(oil_percent_min !== undefined) {
                yield put({ type: 'saveOilPercentMin', payload: oil_percent_min });
            }

            if(order_by !== undefined) {
                yield put({ type: 'saveOrderBy', payload: order_by });
            }

            if(order_type !== undefined) {
                yield put({ type: 'saveOrderType', payload: order_type});
            }

            if(page !== undefined) {
                yield put({ type: 'savePage', payload: page });
            }

            if(Object.keys(params).length) {
                yield put({ type: 'saveParams', payload: params });
            }

        }

        const {
            params,
            state,
            oil_percent_min,
            oil_percent_max,
            order_by,
            order_type,
            page
        } = yield select(state => state.machines);

        const searchParams = {
            state: state,
            oil_percent_min: oil_percent_min,
            oil_percent_max: oil_percent_max,
            order_by: order_by,
            order_type: order_type,
            page: page,
            count_per_page: 10,
            ...params,
        };

        const fetchResponse = yield call(api.fetchMachines, searchParams);

        try {
            const fetchResult = yield call(parseResponse(fetchResponse));
            if(!fetchResult.Code) {
                yield put({ type: 'saveMachines', payload: fetchResult.Result });
            } else {
                throw new MessageException(Error.getError(fetchResult.Code, errors));
            }
        } catch (e) {
            if (e instanceof NetworkException) {
                throw new MessageException('xxxxxxxxxxxxxxxxx !');
            } else {
                throw e;
            }
        }
    }

在已有单元测试之后,我觉得这段代码还不能满足需求,我要检测请求回来的列表的数据是否异常,比如加入请求第 2 页的数据回来发现为空,则继续请求第一页的数据。这个时候如何补上这个用例去指导开发人员完善这个需求?

如果有指导如果写 spa 项目或者测试相关资料书籍,也希望大家推荐一下,谢谢!

3272 次点击
所在节点    问与答
1 条回复
ly710
2017-04-29 12:32:13 +08:00
额。。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/358057

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX