单元测试
单元测试的好处:编写“高内聚低耦合”的代码有了一条统一的实现路径
单元的定义:单元如何定义取决于你如何定义“一件事”。只要这个“单元”里做的是“同一件事”,哪怕是其中定义了3个方法,它也可以是一个“单元”。
比如:写一个下订单的单元测试,可以把生成订单的方法和扣减红包的方法放在一起做单元测试,这样比两个方法分别做单测还可以多做一些关联验证,例如,订单上的红包金额是否与扣减的红包金额一致?
如何判断单元测试的好坏
第一级:大部分代码不需要Mock就可以测试。最优秀
第二级:大部分代码需要Mock才能测试,但都不是静态方法。
第三级:大部分代码需要Mock才能测试,而且包含大量静态方法。(一般的Mock工具还无法Mock静态方法)
确定单元测试的范围
1:公共组件库。这些代码变更不会特别频繁,覆盖率需要尽量达到100%。
2:被调用频次越高的代码。比如:登录验证,权限获取,项目列表获取等。
单元测试的标准
L1:输入正确的参数时,会有正确的输出(测试正确的处理逻辑是否符合预期)
L2:输入错误的参数时,不能抛出系统级额异常。(测试错误的处理逻辑是否符合预期)
L3:极端情况和边界数据可用。可能一开始无法考虑到很多边界条件和极端情况,所以这是一个需要长期维护的部分
L4:覆盖率达到100%
4个标准对应的运用场景:
L1:实在时间紧迫并且代码对应的功能不是核心部分
L2:非核心模块大部分时候应该要达到的标准
L3:核心模块要达到的标准
L4:全局基础框架、封装的非业务型类库要达到的标准
单元测试的数据从哪里来?
不能直连数据库,如果直连数据库,之前的逻辑错误所造成的数据错误,影响本次测试的成果。使用Mock对象
1:涉及到IO的代码和业务代码尽量分开。这里的IO不仅仅时磁盘IO还有网络IO。实现方式也很简单,将IO部分抽象出接口,通过依赖注入的方式调用。这样在写单元测试的时候
可以通过Mock方式来提供一个IO方法的实现。
2:测试数据与测试用例分离。在写单元测试的时候,因为需要考虑很多种情况,所以需要构造好几套测试数据。
@Test void testAdd(){ for(Object[] s : data()){ assertEquals(s[2], (int)s[0]+(int)s[1]); } } public static Iterable<Object[]> data(){ List<Object[]> list = new ArrayList<Object[]>(); list.add(new Object[]{1,1,2}); list.add(new Object[]{-1,1,0}); list.add(new Object[]{0,0,0}); return list; }
这样后续维护测试数据只要在data()方法里进行就好了,确保已发生的bug总是被覆盖在单元测试范围内。