了解过单元测试相关概念的人应该会清楚一个概念:一个好的单元测试应该是与环境无关的,每一个测试都是相互独立的。亦即你可以在任何地方,以任意顺序运行这些测试,最后得到的结果是一样的。但是我被测试的类/方法中本身夹杂着对其它类的依赖,这又该怎么处理呢,将依赖进行 mock 是其中一个做法。本文将记录我在测试过程中的一些备忘,以及遇到的一些问题。

背景说明

我要对我正在开发的一个考试系统中的题目管理部分进行单元测试,这部分主要有一个 SubjectService 接口及其对应的实现类 SubjectServiceImpl,Service 内部又依赖于 DAO 层的两个 Mapper(SubjectMapperSubjectAnswerMapper)。现在我要对 Service 层进行单元测试。此为背景。

过程

首先要确定一个概念:测 Service 层,我们要测它的什么?Service 层对数据库的访问是通过 DAO 层进行的。那么对数据库相关的操作就不适宜放在这里进行测试(对它们的测试应该放在 DAO 层)。Service 层作为主要业务逻辑的载体,对 Service 层的测试应该围绕流程进行(对于不合法的输入,应该抛出对应的异常;对于正常的输入,则流程应该能正常走完,至于数据库访问的正确与否,交给 DAO 层的单元测试进行保证)。

确定了这一点之后,接下来就可以开始测试流程了。首先是引入相关的测试框架。由于项目采用了 SpringBoot,我参考了参考资料中的内容,构建起整个测试环境的依赖。

然后就是开始编写相关的测试类:

@RunWith(MockitoJUnitRunner.class)
public class SubjectServiceImplTest {
private SubjectServiceImpl subjectServiceImpl;
}

对于 Service 所依赖的两个 DAO,只需要创建对应的两个 Mapper 并为其加上 @Mock 注解,然后在被测试对象上加上 @InjectMocks 注解,即完成了对依赖的 mock:

@RunWith(MockitoJUnitRunner.class)
public class SubjectServiceImplTest {
@Mock
private SubjectMapper subjectMapper; @Mock
private SubjectAnswerMapper subjectAnswerMapper; @InjectMocks
private SubjectServiceImpl subjectServiceImpl;
}

然后就可以开始测试我们的 Service 的方法了。由于 Mock 的引入,现在测试方法的整个流程变成了 4 个步骤:

  1. 准备测试用的输入
  2. 给 Mock 对象设置预期的输出(因为被测对象所依赖的是由你虚拟出来的东西,所以依赖应该怎么响应需要你手动设置)
  3. 运行被测方法
  4. 检查运行结果是否与预期一致

以下是一个例子

/**
* 测试插入没有答案的试题
* 应该抛出异常
*/
@Test
public void testSaveSubjectWithoutAnswer() {
SubjectDTO subjectDTO = new SubjectDTO();
subjectDTO.setName("testSubject");
subjectDTO.setDifficulty(1L);
subjectDTO.setCategoryId(1L);
subjectDTO.setSubjectTypeId(1L); Mockito.when(subjectMapper.insert(Mockito.any()))
.thenReturn(1); try {
subjectService.saveSubject(subjectDTO);
} catch (BusinessException e) {
assertEquals(e.getCode(), ResultEnum.INCOMPLETE_ADD_EXERCISE_INFORMATION.getCode());
return;
}
throw new RuntimeException("Should not reach here!");
}

在上面的例子中,步骤 2 使用到了 Mockito 类的一些静态方法,设定了 Mapper 里会被调用方法的响应。(这里建议为了简化代码,可以通过 import static 的方式引入 Mockito 的所有方法,这样可以省略前面的类名)受限于我使用的 JUnit 为 JUnit 4,所以对异常的测试只能这样进行,在 JUnit 5 中就添加了对预期抛出异常的 assert。

会做这个测试,其余的测试也就基本能够进行下去了。

遇到的问题

在跑的过程中,我发现了一个挺棘手的问题,目前还没找到合适的方案。

项目的 DAO 层使用的是 MyBatis + 通用 Mapper 这一套框架。我在 Mock 方法的时候发现在运行的过程中,有关 Mapper 方法中的 selectByExample 的部分总是运行不了,我在方法内部写了创建 Example 的过程,如果使用 Mock,创建 Example 的过程会出现异常,内容大概是要依赖一个数据库环境。所以在不考虑 Service 和 DAO 集成测试的情况下,涉及到这部分的 Service 的部分无法进行测试,后续我会继续查阅相关资料并更新此文。

参考资料

SpringBoot2.x 单元测试 | 闪烁之狐

使用 Mockito 辅助单元测试的更多相关文章

  1. 基于Springboot+Junit+Mockito做单元测试

    前言 前面的两篇文章讨论过< 为什么要写单元测试,何时写,写多细 >和<单元测试规范>,这篇文章介绍如何使用Springboot+Junit+Mockito做单元测试,案例选取 ...

  2. 使用Mockito进行单元测试【2】—— stub 和 高级特性[转]

    一篇中介绍了Mockito的基本信息,现在接着介绍Mockito强大的stub功能 2. Mockito使用实例 5. 对连续的调用进行不同的返回 (iterator-style stubbing) ...

  3. 使用Mockito进行单元测试【1】——mock and verify[转]

    本文转自:http://qiuguo0205.iteye.com/blog/1443344 1. 为什么使用Mockito来进行单元测试? 回答这个问题需要回答两个方面,第一个是为什么使用mock?m ...

  4. 使用 Junit + Mockito 实践单元测试

    一.前言 相信做过开发的同学,都多多少少写过下面的代码,很长一段时间我一直以为这就是单元测试... @SpringBootTest @RunWith(SpringRunner.class) publi ...

  5. Mockito+Junit5单元测试

    参考: https://segmentfault.com/a/1190000006746409 https://waylau.com/mockito-quick-start/ 1.引入依赖 下面这个最 ...

  6. powermock+mockito+testng 单元测试pom文件

    0:Supported versions PowerMock version 1.7.0 and upper has experimental support of Mockito 2. A lot ...

  7. 单元测试系列:Mock工具之Mockito实战

    更多原创测试技术文章同步更新到微信公众号 :三国测,敬请扫码关注个人的微信号,感谢! 原文链接:http://www.cnblogs.com/zishi/p/6780719.html 在实际项目中写单 ...

  8. 单元测试系列之五:Mock工具之Mockito实战

    更多原创测试技术文章同步更新到微信公众号 :三国测,敬请扫码关注个人的微信号,感谢! 原文链接:http://www.cnblogs.com/zishi/p/6780719.html 在实际项目中写单 ...

  9. 单元测试---googletest

    单元测试概述 测试并不只是测试工程师的责任,对于开发工程师,为了保证发布给测试环节的代码具有足够好的质量( Quality ),为所编写的功能代码编写适量的单元测试是十分必要的. 单元测试( Unit ...

随机推荐

  1. 二、NodeJS入门——准备工作(2)——MongoDB安装以及客户端Robomongo安装和使用

    目录     1.介绍     2.下载地址     3.MongoDB安装过程     4.MongoDB的使用     5.MongoDB添加管理员账户     6.RoboMongo安装过程   ...

  2. 实现HTML调用打开本地软件文件

    有时候我们想要实现一个功能,就是在HTML页面点击一个链接就能调用打开本地可执行文件.就像腾讯QQ.迅雷这种. 而实现这种功能其实也很简单,就是需要我们添加修改注册表,实现自定义URL Protoco ...

  3. WorkFlow四:添加用户决策步骤

    沿用之前的例子,做个用户决策步骤. 1.事物代码SWDD: 进入抬头,点击类的绑定按钮. 2.选择类的绑定,点击继续. 这是类的绑定已经变色了.这时候点击保存,再点击返回到图片逻辑流界面. 3.在发送 ...

  4. ubuntu16.04 共享文件夹之后 /mnt/hgfs目录下没有显示共享的文件夹

    root权限执行: apt-get install open-vm-tools vmhgfs-fuse .host:/ /mnt/hgfs

  5. MySQL读写分离之Proxy

    MySQL Proxy: ======================================================== MySQL_Proxy Master Slave1 Slav ...

  6. shell 的 正则表达式

    shell的正则表达式规则 https://www.jb51.net/tools/shell_regex.html 常规字符 字符 描述 \ 将下一个字符标记为一个特殊字符.或一个原义字符.例如,“n ...

  7. 小样本学习(few-shot learning)在文本分类中的应用

    1,概述 目前有效的文本分类方法都是建立在具有大量的标签数据下的有监督学习,例如常见的textcnn,textrnn等,但是在很多场景下的文本分类是无法提供这么多训练数据的,比如对话场景下的意图识别, ...

  8. angularjs中设置select的选中项

    最近用angularjs比较多,里面有很多自己的方法,都不咋会用,这篇只是个笔记,防止自己忘记 <select style="width:100%" ng-model=&qu ...

  9. 01-赵志勇机器学习-Logistics_Regression-train

    Logistics Regression 二分类问题. 模型 线性模型 响应 sigmoid 损失函数(显示) 最小均方 优化方法 BGD 例子: #coding utf-8 import numpy ...

  10. nginx 设置反向代理

    一.多个路径指向同一ip的不同服务 参考地址:https://www.cnblogs.com/hanmk/p/9289069.html 编辑nginx.conf配置文件,新增加一个server模块,或 ...