使用 Mockito 辅助单元测试

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

背景说明

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

过程

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

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

而后就是开始编写相关的测试类:数据库

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

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

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

而后就能够开始测试咱们的 Service 的方法了。因为 Mock 的引入,如今测试方法的整个流程变成了 4 个步骤:app

  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 单元测试 | 闪烁之狐

相关文章
相关标签/搜索