如何写好单测

这篇具有很好参考价值的文章主要介绍了如何写好单测。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

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、编写单元测试

单测怎么写,Java知识总结,单元测试,驱动开发,junit
可以把单元测试编写流程分为四大步骤,八大操作。

定义对象阶段

定义测试对象

在编写单元测试时,首先需要定义被测对象,或直接初始化、或通过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 {
	@Spy
	private UserService userService = new UserService();
}
  • 利用@InjectMocks注解
@RunWith(PowerMockRunner.class)
public class UserServiceTest {
	@InjectMocks
	private UserService userService;
}
模拟依赖对象

在编写单元测试用例时,需要模拟各种依赖对象——类成员、方法参数和方法返回值。

  • 直接构建对象
UserDO user = new User(1L, “test”);
List<Long> userIdList = Arrays.asList(1L, 2L, 3L);
  • 反序列化对象
UserDO user = JSON.parseObject(text, UserDO.class);
List<UserDO> userList = JSON.parseArray(text, UserDO.class);
Map<Long, UserDO> userMap = JSON.parseObject(text, new TypeReference<Map<Long, UserDO>>() {});
  • 利用Mockito.mock方法
MockClass mockClass = Mockito.mock(MockClass.class);
List<Long> userIdList = (List<Long>)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 {
    // 模拟依赖对象(类成员)
	@Mock
	private UserDAO userDAO;
	
	// 定义测试对象
	@InjectMocks
	private UserService userService;
   
   @Before
   public void before() {
   		// 输入依赖对象(类成员)
		Whitebox.setInternalState(userService, "canModify", true);
   }
}

模拟方法阶段

在编写单元测试用例时,需要模拟方法指定参数并返回指定值。
单测怎么写,Java知识总结,单元测试,驱动开发,junit

举个例子

模拟依赖对象的数据可以自己构建、Mock或者可以从资源文件里读取。

@Test
public void testCreateUserWithCreate {
    // 模拟依赖对象方法:getIdByName
 	Mockito.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());
}

调用方法阶段

单测怎么写,Java知识总结,单元测试,驱动开发,junit

验证方法阶段

  • 验证依赖方法
    单测怎么写,Java知识总结,单元测试,驱动开发,junit
  • 验证数据对象
    单测怎么写,Java知识总结,单元测试,驱动开发,junit
  • 验证依赖对象
    单测怎么写,Java知识总结,单元测试,驱动开发,junit

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、常见单测问题

在编写单元测试用例时,或多或少会遇到一些问题,大多数是由于对测试框架特性不熟悉导致,比如:文章来源地址https://www.toymoban.com/news/detail-740358.html

  • 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.*”})注解。

到了这里,关于如何写好单测的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Go如何优雅的写数据库的单测

    ​当你想在代码中测试 Gorm 时,可以考虑使用单元测试或集成测试来确保 Gorm 的功能正常。下面是一个简单的示例,展示了如何编写一个基本的 Gorm 单元测试。 app config config.yaml conf.go services project.go project_test.go init_test.go cmd main.go rootDir, err := os.Getwd() : os.Getwd() 函数用于获取当

    2024年02月03日
    浏览(52)
  • 如何写好测试用例

    测试出发点 :要证明所测的程序是错误的。 用例设计目标 :尽可能的发现bug。 用例设计内容 :待测试的功能点、测试环境、测试数据、执行操作、预期结果。 测试环境的设计 :设计真实而危险的环境,不忽视偶发环境。 用例的设计 :包含正向的预期结果和有意导致失败

    2024年02月13日
    浏览(36)
  • 如何写好新闻稿

    写好新闻稿是一门技巧和艺术的结合。一个有效的新闻稿应该能够快速吸引读者的注意力,并为他们提供有价值的信息。以下是如何写好新闻稿的步骤和建议: 1.吸引眼球的标题 简短明了:标题应该简洁,一眼就能告诉读者新闻的核心内容。 使用动词:动词可以带来更强的

    2024年02月11日
    浏览(41)
  • SpringBoot如何写好单元测试

    Spring中的单元测试非常方便,可以很方便地对Spring Bean进行测试,包括Controller、Service和Repository等Spring Bean进行测试,确保它们的功能正常,并且不会因为应用的其他变化而出现问题。 1. 导入所需的依赖 :在测试类中,需要导入Spring Test相关的依赖,例如spring-test和JUnit。 2.

    2024年03月23日
    浏览(36)
  • 03|「如何写好一个 Prompt」

    Prompt       指令(角色)+ 生成主体 + 额外要求 指令:模型具体完成的任务描述。例如,翻译一段文字,翻译即指令。位置:一般放在模板的最前面; 角色:扮演角色的描述。例如,作为一个期刊审稿人,期刊审稿人即角色; 利用角色完成一个具体的任务; 生成主体:对

    2024年02月15日
    浏览(46)
  • 路由器指示灯不亮了怎么办 路由器上的信号指示灯不亮的解决办法总结

    最近,接到不少网友的反应。说自己的无线路由器信号指示灯不亮,刚开始以为是路由器坏额,后来检测发现不是路由器的问题。今天小编就总结一下导致这种情况可能存在的问题,当然额,不排除小编总结的方法不全面。呵呵! 路由器信号指示灯 一、信号指示灯不亮 信号

    2024年02月06日
    浏览(51)
  • 如何将写好的Python代码,封装运行?

    要把Python代码封装成可执行的程序可以通过以下步骤完成: 首先将代码保存为.py文件 然后在代码中添加适当的命令行参数解析器(如argparse),使得代码可以通过命令行接受输入参数 之后再在代码的开头添加#!/usr/bin/env python,这将允许脚本在Unix/Linux/Mac系统中以可执行文件的

    2024年02月08日
    浏览(46)
  • 【全方位解析】如何写好技术文章

    前言 为何而写 技术成长 :相对于庞大的计算机领域的知识体系,人的记忆还是太有限了,而且随着年龄的增大,记忆同样也会逐渐衰退,正如俗话所说“好记性不如烂笔头”。并且在分享博客的过程中,我们也可以和大神交流,进而发现自己的认知错误,纠正知识体系。最

    2024年02月16日
    浏览(46)
  • Midjourney干货篇 - 与AI对话,如何写好prompt

    木匠不会因为电动工具的出现而被淘汰,反而善用工具的木匠,收入更高了。 想要驾驭好Midjourney,可以从以下方面出发调整: 首先 Midjourney 基本上是不懂语法的,所以即使你语法错了,只要词对了,也能生成图片,所以prompt 不是越长越好。可以把指令用逗号隔开,一个个输

    2024年02月06日
    浏览(56)
  • 非行稳无以致远:华为如何写好数字金融的大文章?

    在大力发展实体经济,推进新型工业化,加快建设制造强国、质量强国、航天强国、交通强国、网络强国、数字中国的时代背景下,一项工作的重要性正在与日俱增——那就是金融。 在现代化国家的建设过程中,金融是服务实体经济,保障新型工业化建设的基础,其价值不言

    2024年02月03日
    浏览(33)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包