本文是自己对学习的一个总结
了解到什么是Mockito,并且配置好Mockito的maven之后,我们就可以开始编写一个最基本的Mockito测试类了。如果对这两个内容不太了解,可以查看这篇文章https://blog.csdn.net/sinat_38393872/article/details/106520371。
Mockito是在Spring的框架中使用的单元测试,我们先新建一个Spring的项目,项目结构如下所示。
这是一个典型的Spring项目结构,Service层是我们想要测试的类。Service的代码依赖Dao层的类,我们将在测试中模拟dao层的代码,不让dao层的代码真正执行。
我们先看看StudentServiceImpl的代码
public class StudentServiceImpl implements StudentService {
@Autowired
private StudentDao studentDao;
/**
* 这是准备测试的代码,其中的studentDao.insert方法我们要模拟执行
*/
@Override
public boolean insert(Integer id) {
if(studentDao.insert(id) >= 1) {
return true;
} else {
return false;
}
}
}
我们再看看StudentServiceImpl依赖的StudentDao的代码。
public class StudentDao {
public int insert(Integer id) {
System.out.println("执行了真正的操作");
return 1;
}
}
额·····这个代码不是真正地操作数据库,先别在意这个。总之,这段代码真正执行的话,应该要在控制台输出"执行了真正的操作"这么一行字,我们等会测试的时候查看控制台有没有输入相应的内容就知道这段代码有没有被真正执行了。
测试文档的项目结构应该与业务代码的一致。测试类和被测试类的相对位置应该是一样的。同时,测试类的命名建议为”被测试类+Test"。
比如上面的被测试类是StudentService,对应的测试类就是StudentServiceTest。StudentService的目录是com.example.mokitoLearning.student.service.StudentService,StudentServiceTest的目录也是com.example.mokitoLearning.student.service.StudentServiceTest。
除此之外,测试方法的命名也建议是“被测试方法 + Test”。比如我们测试StudentService中的insert方法,那我们的测试方法就应该写在StudentServiceTest之下,方法名为insertTest。
确定好项目结构以后,我们在测试类中开始写测试方法。 测试类不会自己知道我们想测试哪个类,所以我们使用@InjectMocks标注一下。
public class StudentServiceTest {
@InjectMocks
private StudentServiceImpl studentService;
//其他操作
}
@InjectMocks标注的类型不能是接口,所以我标注的是StudentService的实现类。
被测试类的依赖是需要注入的。我们使用@Mock进行注入。@Mock标记的变量会被注入到@InjectMocks标注的变量中。其用法和@Autowired一样。
public class StudentServiceTest {
@InjectMocks
private StudentServiceImpl studentService;
@Mock
private StudentDao studentDao;
}
测试方法执行前,必须先初始化Mockito。初始化Mockito是这样的。
MockitoAnnotations.initMocks(this);
我们现在先别管这里面做了什么,只要知道测试方法执行前,必须先执行这一条语句。
这样的话,每个方法的第一句我们就要先写这么一条语句,这有些麻烦。我们可以利用@Before注解让程序简洁一些。
@Before用来标注一个public void方法,这个被标注的方法会在所有的测试方法运行前运行。通常在@Before标注的方法中定义一些公共逻辑,比如初始化MockitoAnnotations.initMocks(this);就是其中之一。使用@Before后,我们就不用在每一个测试方法中都写初始化Mockito的语句了。
public class StudentServiceTest {
@InjectMocks
private StudentServiceImpl studentService;
@Mock
private StudentDao studentDao;
/**
* 不用在意方法名是什么,只要方法是public void的就可以
*/
@Before
public void init() {
MockitoAnnotations.initMocks(this);
}
}
另外,我们可以定义多个@Before标注的方法。
我们使用@Test标记一个方法是测试方法,标记以后这个方法才可以被测试。项目启动时,系统会运行@Test标注的方法,方法中出错的话(逻辑不符合我们的预期),项目会无法启动,这时候我们就能知道项目的代码是有问题的。这样就能帮助我们快速知道问题所在,也能提前知道项目存在问题,避免上线隐患。
public class StudentServiceTest {
@InjectMocks
private StudentServiceImpl studentService;
@Mock
private StudentDao studentDao;
@Before
public void init() {
MockitoAnnotations.initMocks(this);
}
@Test
public void insertTest() {
//测试逻辑
}
}
单元测试中,不能真正执行依赖的外部逻辑。我们必须模拟执行。
在Mockito中,我们可以使用
Mockito.when().thenReturn();
这样的语句来模拟依赖逻辑。这个方法应该在测试方法的开始部分就写明。这个方法表示测试逻辑运行时,到了要调用某一个方法的时候,不去执行这个方法,直接返回一个值。我们看看示例。
Mockito.when(studentDao.insert(Mockito.any())).thenReturn(3);
这个语句之后,我们测试执行studentDao的insert方法时,一遇到studentDao.insert()方法时,系统就不会去真正执行这个方法,而是模拟执行,直接返回3。我们还可以设置为不返回值,而是抛出异常。更多的用法可以看官方文档https://javadoc.io/doc/org.mockito/mockito-core/latest/org/mockito/Mockito.html#34。
到了这里我们就应该能写出一个基本的测试了。
public class StudentServiceTest {
@InjectMocks
private StudentServiceImpl studentService;
@Mock
private StudentDao studentDao;
@Before
public void init() {
MockitoAnnotations.initMocks(this);
}
@Test
public void insertTest() {
Mockito.when(studentDao.insert(Mockito.any())).thenReturn(3);
//执行到studentDao.insert方法是,直接返回3,那么下面这条语句的断言应该不会报错
Assert.assertEquals(true, studentService.insert(1));
System.out.println(studentService.insert(4));
}
}
执行测试方法,输出结果如下
没有输出"执行了真正的操作",所以studentDao.insert方法确实没有真正执行,模拟成功。