对于使用 react 全家桶( react+redux-saga)作为技术栈的前端项目,大家是怎么进行单元测试和以测试作为驱动的开发的? - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
ly710
V2EX    问与答

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

  •  
  •   ly710 2017-04-29 01:00:57 +08:00 3499 次点击
    这是一个创建于 3109 天前的主题,其中的信息可能已经有所发展或是发生改变。

    最近我司在大力推广单元测试和测试驱动的开发方式(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 项目或者测试相关资料书籍,也希望大家推荐一下,谢谢!

    1 条回复    2017-04-29 12:32:13 +08:00
    ly710
        1
    ly710  
    OP
       2017-04-29 12:32:13 +08:00
    额。。
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     894 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 24ms UTC 19:39 PVG 03:39 LAX 12:39 JFK 15:39
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86