网站一直建设中,工程监理行业为什么做网站,没钱可以开网店吗,怎么制作头像1、为什么要写单测#xff1f;
单测即单元测试#xff08;Unit Test#xff09;#xff0c;是对软件的基本组成单元进行的测试#xff0c;比如函数、过程或者类的方法。其意义是#xff1a;
功能自测#xff0c;发现功能缺陷自我Code Review测试驱动开发促进代码重构并…1、为什么要写单测
单测即单元测试Unit Test是对软件的基本组成单元进行的测试比如函数、过程或者类的方法。其意义是
功能自测发现功能缺陷自我Code Review测试驱动开发促进代码重构并提升代码质量
1.1、代码覆盖率
单测质量最直接表现的指标就是代码覆盖率分为语句覆盖Statement coverage、分支覆盖Branch coverage、条件覆盖Condition converage、路径覆盖Path coverage
1.2、单元测试 VS 集成测试
系统上线前都会做回归测试和集成测试但为什么还要加单元测试呢
指标对象单元测试集成测试测试对象程序单元模块组合测试方法白盒测试黑盒测试测试时间开发阶段集成阶段测试内容代码逻辑接口功能测试粒度较细粒度较粗粒度
2、如何写好单测
2.1、单测规约
可以参考阿里巴巴 的Java开发规范以下几点在单测中要特别关注
【强制】好的单测必须遵守AIR原则。说明单元测试在线上运行时像空气一样感觉不到但在测试的质量保证上却是非常关键的。好的单元测试宏观上说具体有自动化(Automatic)、独立性(Idependent)、可重复执行(Repeatable)的特点。【强制】单元测试应该是全自动执行的并且非交互式的。测试用例通常是被定期执行的执行过程必须完全自动化才有意义。输出结果需要人工检查的测试不是一个好的单元测试。单元测试中不准使用System.Out来进行人肉验证必须使用Assert来验证。【强制】单元测试是可以重复执行的不能受到外界环境的影响。【推荐】编写单元测试代码遵守BCDE原则以保证被测试模块的交付质量。 B: Border边界值测试包括循环边界、特殊取值、特殊时间点、数据顺序等。C: Correct正确的输入并得到预期的结果。D: Design与设计文档相结合来编写单元测试。E: Error强制错误信息输入(如:非法数据、 异常流程、业务允许外等)并得到预期的结果
2.2、一把好工具
写单侧首先要有好的单测工具常用工具: Mockito、PowerMock、 EasyMock、JMockito等Mock可以解决
解除对外部服务依赖减少全链路测试的数据准备模拟一些非正常的流程不用加载项目环境配置实现模块之间的并行开发
2.3、编写单元测试 可以把单元测试编写流程分为四大步骤八大操作。
定义对象阶段
定义测试对象
在编写单元测试时首先需要定义被测对象或直接初始化、或通过Spy包装…实例化。
直接构建对象 UserService userService new UserService();利用Mockito.spy方法 UserService userService Mockito.spy(new UserService()); UserService userService Mockito.spy(UserService.class);利用Spy注解
RunWith(PowerMockRunner.class)
public class CompanyServiceTest {Spyprivate UserService userService new UserService();
}利用InjectMocks注解
RunWith(PowerMockRunner.class)
public class UserServiceTest {InjectMocksprivate UserService userService;
}模拟依赖对象
在编写单元测试用例时需要模拟各种依赖对象——类成员、方法参数和方法返回值。
直接构建对象
UserDO user new User(1L, “test”);
ListLong userIdList Arrays.asList(1L, 2L, 3L);反序列化对象
UserDO user JSON.parseObject(text, UserDO.class);
ListUserDO userList JSON.parseArray(text, UserDO.class);
MapLong, UserDO userMap JSON.parseObject(text, new TypeReferenceMapLong, UserDO() {});利用Mockito.mock方法
MockClass mockClass Mockito.mock(MockClass.class);
ListLong userIdList (ListLong)Mockito.mock(List.class);
利用Mock注解 Mock private UserDAO userDAO;利用Mockito.spy方法 UserService userService Mockito.spy(new UserService());利用Spy注解 Spy private UserService userService new UserService(); // 必须初始化
注入依赖对象
在编写单元测试用例时需要模拟各种依赖对象——类成员、方法参数和方法返回值。
利用Setter方法注入 userService.setMaxCount(100); userService.setUserDAO(userDAO);利用ReflectionTestUtils.setField方法注入 ReflectionTestUtils.setField(userService, “maxCount”, 100); ReflectionTestUtils.setField(userService, “userDAO”, userDAO);利用Whitebox.setInternalState方法注入 Whitebox.setInternalState(userService, “maxCount”, 100); Whitebox.setInternalState(userService, “userDAO”, userDAO);利用InjectMocks注解注入 Mock private UserDAO userDAO; InjectMocks private UserService userService;设置静态常量字段值 FieldHelper.setStaticFinalField(UserService.class, “log”, log);
举个例子
RunWith(PowerMockRunner.class)
public class UserSericeTest {// 模拟依赖对象类成员Mockprivate UserDAO userDAO;// 定义测试对象InjectMocksprivate UserService userService;Beforepublic void before() {// 输入依赖对象类成员Whitebox.setInternalState(userService, canModify, true);}
}模拟方法阶段
在编写单元测试用例时需要模拟方法指定参数并返回指定值。
举个例子
模拟依赖对象的数据可以自己构建、Mock或者可以从资源文件里读取。
Test
public void testCreateUserWithCreate {// 模拟依赖对象方法getIdByNameMockito.doReturn(null).when(userDAO).getIdByName(Mockito.anyString());Long mockUserId 2L;// 从资源文件加载String jsonData ResourceHelper.getResouceAsString(getClass(), path /data.json)UserDO userDO JSON.parseObject(jsonData, UserDO.class);// Long userId userService.createUser(userDO);Assert.assertEquals(用户标识不一致, mockUserId, userId);// 验证依赖方法Mockito.verify(userDAO).getIdByName(userDO.getUserName());
}调用方法阶段 验证方法阶段
验证依赖方法 验证数据对象 验证依赖对象
3、 如何做的更好
写代码不只是乱写一通覆盖率上去了就可以了它本质也是代码也要符合代码规约。一个好的单测命名可以帮助理清单测Case 也可以便于他人Review。
3.1、规范命名
测试类命名 按照行业惯例测试类的命名应以被测试类名开头并以Test结尾。 比如:UserServiceTest(用户服务测试类)测试方法命名 按照行业规范测试方法命名应以test开头并以被测试方法结尾。 a) 按照结果命名 • testBatchCreateWithSuccess(测试:批量创建-成功) • testBatchCreateWithFailure(测试:批量创建-失败) • testBatchCreateWithException(测试:批量创建-异常) b) 按照参数命名 • testBatchCreateWithListNull(测试:批量创建-列表为NULL) • testBatchCreateWithListEmpty(测试:批量创建-列表为空) • testBatchCreateWithListNotEmpty(测试:批量创建-列表不为空) c) 按照意图命名 • testBatchCreateWithNormal(测试:批量创建-正常) • testBatchCreateWithGray(测试:批量创建-灰度) • testBatchCreateWithException(测试:批量创建-异常)测试资源命名-语义化 建议优先使用这些参数和变量的名称并加后缀“.json”标识文件格式。 比如:userCreateList.json
3.2、各环节做好验证
不验证返回值 不验证返回值怎么能保证方法返回了正确值?不验证方法调用 不验证方法调用怎么能保方法被正确的调用? Ø 不验证方法参数 不验证方法参数怎么能保证传递数据的正确性? Ø 不验证异常信息 不验证异常信息怎么能保证抛出异常的正确性?
4、常见单测问题
在编写单元测试用例时或多或少会遇到一些问题大多数是由于对测试框架特性不熟悉导致比如:
Mockito不支持对静态方法、构造方法、final方法、私有方法的模拟应该使用PowerMock功能;Mockito的any相关的参数匹配方法并不支持可空参数和空参数应该使用nullable方法;未Mock方法或Mock方法参数不匹配时会返回默认值(基础类型为0对象类型为null);采用Mockito的参数匹配方法时其它参数不能直接用常量或变量应该使用Mockito的eq方法;采用Argument的captor方法时其它参数不能直接用常量或变量应该使用Mockito的eq方法;使用when-then语句模拟Spy对象方法会先执行真实方法应该使用do-when语句;PowerMock对静态方法、构造方法、final方法、私有方法的模拟需要把对应的类添加到 PrepareForTest注解中;PowerMock模拟JDK的静态方法、构造方法、final方法、私有方法时需要把使用这些方法的类 加入到PrepareForTest注解中但会导致单元测试覆盖率不被统计;PowerMock使用自定义的类加载器来加载类可能导致系统类加载器认为有类型转化问题;需要加上PowerMockIgnore({“javax.crypto.*”})注解。