使用 Junit + Mockito 实践单元测试
一、前言
相信做过开发的同学,都多多少少写过下面的代码,很长一段时间我一直以为这就是单元测试...
@SpringBootTest
@RunWith(SpringRunner.class)
public class UnitTest1 {
@Autowired
private UnitService unitService;
@Test
public void test() {
System.out.println("----------------------");
System.out.println(unitService.sayHello());
System.out.println("----------------------");
}
}
但这是单元测试嘛?unitService 中可能还依赖了 Dao 的操作;如果是微服务,可能还要起注册中心。那么这个“单元”也太大了吧!如果把它称为集成测试,可能更恰当一点,那么有没有可能最小粒度进行单元测试嘛?
单元测试应该是一个带有隔离性的功能测试。在单元测试中,应尽量避免其他类或系统的副作用影响。
单元测试的目标是一小段代码,例如方法或类。方法或类的外部依赖关系应从单元测试中移除,而改为测试框架创建的 mock 对象来替换依赖对象。
单元测试一般由开发人员编写,通过验证或断言目标的一些行为或状态来达到测试的目的。
二、JUnit 框架
JUnit 是一个测试框架,它使用注解来标识测试方法。JUnit 是 Github 上托管的一个开源项目。
一个 JUnit 测试指的是一个包含在测试类中的方法,要定义某个方法为测试方法,请使用 @Test 注解标注该方法。该方法执行被测代码,可以使用 JUnit 或另一个 Assert 框架提供的 assert 方法来检查预期结果与实际结果是否一致,这些方法调用通常称为断言或断言语句。
public class UnitTest2 {
@Test
public void test() {
String sayHello = "Hello World";
Assert.assertEquals("Hello World", sayHello);
}
}
以下是一些常用的 JUnit 注解:
| 注解 | 描述 |
|---|---|
| @Test | 将方法标识为测试方法 |
| @Before | 在每次测试之前执行。用于准备测试环境(例如,读取输入数据,初始化类) |
| @After | 每次测试之后执行。用于清理测试环境(例如,删除临时数据,恢复默认值) |
| @BeforeClass | 用于 static方法,在所有测试开始之前执行一次。它用于执行耗时的活动,例如:连接到数据库 |
| @AfterClass | 用于 static方法,在完成所有测试之后,执行一次。它用于执行清理活动,例如:与数据库断开连接 |
| @Ignore | 指定要忽略的测试 |
| @Test(expected = Exception.class) | 如果该方法未引发命名异常,则失败 |
| @Test(timeout=100) | 如果该方法花费的时间超过100毫秒,则失败 |
以下是一些常用的 Assert 断言:
| 声明 | 描述 |
|---|---|
| fail([message]) | 使方法失败。在执行测试代码之前,可用于检查未到达代码的特定部分或测试失败 |
| assertTrue([message,]布尔条件) | 检查布尔条件是否为真 |
| assertFalse([message,]布尔条件) | 检查布尔条件是否为假 |
| assertEquals([message,]预期,实际) | 测试两个值是否相同。注意:对于数组,会检查引用而不是数组的内容 |
| assertNull([message,]对象) | 检查对象是否为空 |
| assertNotNull([message,]对象) | 检查对象是否不为空 |
| assertSame([message,]预期,实际) | 检查两个变量是否引用同一对象 |
| assertNotSame([message,]预期,实际) | 检查两个变量是否引用了不同的对象 |
三、Mockito 框架
从上面的介绍我们可以认识到,如何减少对外部的依赖才是实践单元测试的关键。而这正是 Mockito 的使命,Mockito 是一个流行的 mock 框架,可以与 JUnit 结合使用,Mockito 允许我们创建和配置 mock 对象,使用 Mockito 将大大简化了具有外部依赖项的类的测试开发。spring-boot-starter-test 中默认集成了 Mockito,不需要额外引入。
在测试中使用 Mockito,通常会:
- mock 外部依赖关系并将 mock 对象插入待测代码
- 执行被测代码
- 验证代码是否正确执行

3.1 使用 Mockito 创建 mock 对象
Mockit o提供了几种创建 mock 对象的方法:
- 使用静态 mock() 方法
- 使用 @Mock 注解
如果使用 @Mock 注解,则必须触发创建带有 @Mock 注解的对象。使用 MockitoRule 可以做到,它通过调用静态方法 MockitoAnnotations.initMocks(this) 来填充带 @Mock 注解的字段。或者可以使用 @RunWith(MockitoJUnitRunner.class)。
public class UnitTest3 {
// 触发创建带有 @Mock 注解的对象
@Rule public MockitoRule mockitoRule = MockitoJUnit.rule();
// 1. 使用 @Mock 注解创建 mock 对象
@Mock private UnitDao unitDao;
@Test
public void test() {
// 2. 使用静态 mock() 方法创建 mock 对象
Iterator iterator = mock(Iterator.class);
// when...thenReturn / doReturn...when 模拟依赖调用
when(iterator.next()).thenReturn("hello");
doReturn(1).when(unitDao).delete(anyLong());
// 断言
Assert.assertEquals("hello", iterator.next());
Assert.assertEquals(new Integer(1), unitDao.delete(1L));
}
}
3.2 使用 mock 对象实践单元测试
我们要单元测试的内容,常常包含着对数据库的访问等等,那么我们要如何 mock 掉这部分调用呢?我们可以使用 @InjectMocks 注解创建实例并使用 mock 对象进行依赖注入。
@Service
public class UnitServiceImpl implements UnitService {
@Autowired
private UnitDao unitDao;
@Override
public String sayHello() {
Integer delete = unitDao.delete(1L);
System.out.println(delete);
return "hello unit";
}
}
@RunWith(MockitoJUnitRunner.class)
public class UnitTest2 {
@Mock
private UnitDao unitDao;
@InjectMocks
private UnitServiceImpl unitService;
@Test
public void unitTest() {
// mock 调用
when(unitDao.delete(anyLong())).thenReturn(1);
Assert.assertEquals("hello unit", unitService.sayHello());
}
}
Mockito 还有很多有趣的实践,比如:@Spy或spy()方法、verify()验证等等,鉴于篇幅原因,读者可自行挖掘。
3.3 使用 PowerMock mock 静态方法。
Mockito 也有一些局限性。例如:不能 mock 静态方法和私有方法。有关详细信息,请参阅 Mockito限制的常见问题解答。这个时候我们就要用到 PowerMock,PowerMock 支持 JUnit 和 TestNG,扩展了 EasyMock 和 Mockito 框架,增加了mock static、final 方法的功能。
首先需要引入 PowerMock 的依赖:
<!-- PowerMock -->
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-junit4</artifactId>
<version>2.0.7</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-api-mockito2</artifactId>
<version>2.0.7</version>
</dependency>
接下来就能愉快的 mock 静态方法了。
@RunWith(PowerMockRunner.class)
@PrepareForTest({StringUtils.class})
public class UnitTest4 {
@Test
public void test() {
mockStatic(StringUtils.class);
when(StringUtils.getFilename(anyString())).thenReturn("localhost");
Assert.assertEquals("localhost", StringUtils.getFilename(""));
}
}
使用 Junit + Mockito 实践单元测试的更多相关文章
- 基于Springboot+Junit+Mockito做单元测试
前言 前面的两篇文章讨论过< 为什么要写单元测试,何时写,写多细 >和<单元测试规范>,这篇文章介绍如何使用Springboot+Junit+Mockito做单元测试,案例选取 ...
- Springboot单元测试Junit深度实践
Springboot单元测试Junit深度实践 前言 单元测试的好处估计大家也都知道了,但是大家可以发现在国内IT公司中真正推行单测的很少很少,一些大厂大部分也只是在核心产品推广单测来保障质量,今天这 ...
- JUnit + Mockito 单元测试(二)
摘自: http://blog.csdn.net/zhangxin09/article/details/42422643 版权声明:本文为博主原创文章,未经博主允许不得转载. 目录(?)[-] 入门 ...
- JUnit + Mockito 单元测试(二)(good)
import org.junit.Test; import org.mockito.Matchers; import org.mockito.Mockito; import java.util.Lis ...
- JUnit + Mockito 单元测试
原 JUnit + Mockito 单元测试(二) 2015年01月05日 17:26:02 sp42a 阅读数:60755 版权声明:本文为博主原创文章,未经博主允许不得转载. https://bl ...
- intellij JUnit mockito
在intellij越来越普及的情况下,利用JUnit在intellij中进行测试就显得很基础了,但网上的资料总有误导的地方,这里记录一下. 总体而言,要开始单元测试,可以分为三步,添加相关的插件,添加 ...
- Junit mockito解耦合测试
Mock测试是单元测试的重要方法之一. 1.相关网址 官网:http://mockito.org/ 项目源码:https://github.com/mockito/mockito api:http:/ ...
- JUnit+Mockito结合测试Spring MVC Controller
[本文出自天外归云的博客园] 概要简述 利用JUnit结合Mockito,再加上spingframework自带的一些方法,就可以组合起来对Spring MVC中的Controller层进行测试. 在 ...
- 使用 Mockito 辅助单元测试
了解过单元测试相关概念的人应该会清楚一个概念:一个好的单元测试应该是与环境无关的,每一个测试都是相互独立的.亦即你可以在任何地方,以任意顺序运行这些测试,最后得到的结果是一样的.但是我被测试的类/方法 ...
随机推荐
- MATLAB 动图绘制、保存
动图有gif格式和视频的avi格式. 1.sin(x)动图 clear all h = animatedline;%动画线 axis([0 4*pi -1 1]) box on x = linspac ...
- E - 不爱学习的lyb HDU - 1789(贪心策略)
众所周知lyb根本不学习.但是期末到了,平时不写作业的他现在有很多作业要做. CUC的老师很严格,每个老师都会给他一个DDL(deadline). 如果lyb在DDL后交作业,老师就会扣他的分. 现在 ...
- 使用Pytorch在多GPU下保存和加载训练模型参数遇到的问题
最近使用Pytorch在学习一个深度学习项目,在模型保存和加载过程中遇到了问题,最终通过在网卡查找资料得已解决,故以此记之,以备忘却. 首先,是在使用多GPU进行模型训练的过程中,在保存模型参数时,应 ...
- Docker基础修炼2--Docker镜像原理及常用命令
通过前文的讲解对Docker有了基本认识之后,我们开始进入实战操作,本文先演示Docker三要素之镜像原理和相关命令. 本文的演示环境仍然沿用上一篇文章在本地Centos7中安装的环境,如果你本地没有 ...
- WireShark数据包分析一:认识WireShark
一.认识WireShark WireShark是一款抓包软件,官方网址:WireShark.org 官网如下图: 选择Download,在官网下载安装WireShark即可. WireShark可用来 ...
- (js描述的)数据结构[链表](4)
(js描述的)数据结构 [链表](4) 一.基本结构 二.想比于数组,链表的一些优点 1.内存空间不是必须连续的,可以充分利用计算机的内存,事项灵活的内存动态管理. 2.链表不必再创建时就确定大小,并 ...
- 7.5 this关键字的使用;标准学生类的编写 、构造方法的格式
/** 学生类** 起名字我们要求做到见名知意.* 而我们现在的代码中的n和a就没有做到见名知意,所以我要改进.** 如果有局部变量名和成员变量名相同,在局部使用的时候,采用的是就近的原则. * 我们 ...
- Python操作rabbitmq系列(三):多个接收端消费消息
接着上一章.这一章,我们要将同一个消息发给多个客户端.这就是发布订阅模式.直接看代码: 发送端: import pikaimport sys connection = pika.BlockingCon ...
- tf.train.MomentumOptimizer 优化器
tf.train.MomentumOptimizer( learning_rate, momentum, use_locking=False, use_nesterov=False, name='Mo ...
- 对象中属性 加锁 用:volatile 关键词修饰 而 不用 synchronized 加锁
一个对象中有一个状态 属性,现在业务需求 存在多线程来修改 和 拿去 这个状态 的值,这种情况如果加锁怎么加? 一种是 在 set 和get 这个状态的 方法那加 synchronized . 还有一 ...