文档章节

对代码更有信心--单元测试工具Mockito简单介绍

zgw06629
 zgw06629
发布于 2015/04/27 12:03
字数 1064
阅读 439
收藏 7

许多同事怕写单元测试的一个主要原因(或借口)就是依赖太多(redis,mysql,webservice等)。但其实可以用Mockito来模拟相关行为,不用费力去准备各种依赖环境,这时只需专注于业务逻辑即可。

比如有这么一个业务需求,若用户已经购买了容量(20GB,200GB),直接返回容量,若未购买,再判断该用户是否存在,若不存在,提示该用户不存在;若存在则判断是否为激活用户,若为激活用户返回2GB, 若为非激活用户返回1GB。

controller代码如下:

@Autowired
private StorageService storageService;
@Autowired
private UserService userService;
@RequestMapping({ "/storage/userId/{userId}" })
@ResponseBody
public CommonResultDTO getUserStorage(@PathVariable("userId") String userId){
    CommonResultDTO result = new CommonResultDTO();
    Integer capacity = storageService.getUserStorage(userId); //查询用户是否购买了容量
     
    if(capacity==null){ //没有购买
        User user = userService.getUser(userId); //查询该用户
        if(user==null){ //用户不存在
            result.setStatus(FAILURE_STATUS);
            result.setMessage(USER_NOT_EXIST);
        }
        else if(user.getStatus()==ACTIVE) //激活用户 默认返回2GB
            capacity = DEFAULT_ACTIVE_USER_CAPACITY;
        else if(user.getStatus() == INACTIVE) //未激活用户 默认返回1GB
            capacity = DEFAULT_INACTIVE_USER_CAPACITY;
    }
    result.setData(capacity);
    return result;
}

假如只想测试上述业务逻辑,而不想费力去开启本地数据库,并准备各种数据,如插入一条用户购买记录(insert user_storage (user_id, product_id) values(1, 1)),插入一个激活用户(insert into user(user_id, status) values(1, 0)),一个非激活用户。若上述业务还用到了缓存,那准备的工作就更多了.。估计此时大部分开发人员已经打消了写单元测试的念头了,并祈祷这段代码能平平安安。

于是轻易地就把命运交给老天了。

幸好有了Mockito框架,命运又可以掌握在自己手中了,它提供了易用的Api可以让我们仅需关注主要业务逻辑而无需耗费精力在准备繁琐的测试环境上。

见如下示例代码:

@RunWith(MockitoJUnitRunner.class)
public class StorageControllerTest {
    @InjectMocks
    private StorageController controller;
    @Mock
    private UserService userService;
    @Mock
    private StorageService storageService;
    @Test
    public void test_user_not_exist(){
        //1. 验证用户不存在
        String userId = "notexist";
        // 当执行storageService.getUserStorage(userId)时直接返回空 表示未购买
        Mockito.when(storageService.getUserStorage(userId)).thenReturn(null);
        // 当执行userService.getUser(userId)时 返回空 表示用户不存在
        Mockito.when(userService.getUser(userId)).thenReturn(null);
         
        CommonResultDTO result = controller.getUserStorage(userId);
        Assertions.assertThat(result.getStatus()).isEqualTo(FAILURE_STATUS);
        Assertions.assertThat(result.getMessage()).isEqualTo(USER_NOT_EXIST);
        //验证是否执行了storageService.getUserStorage(userId)一次;
        Mockito.verify(storageService).getUserStorage(userId);
        //验证是否执行了userService.getUser(userId)一次;
        Mockito.verify(userService).getUser(userId);
    }
    @Test
    public void test_inactive_and_not_buy_user_storage(){
        //2. 验证未购买且未激活用户的返回容量
        // 定义一未激活状态用户
        String userId = "1";
        User user = new User();
        user.setStatus(INACTIVE);
        // 当执行storageService.getUserStorage(userId)时直接返回空 表示未购买
        Mockito.when(storageService.getUserStorage(userId)).thenReturn(null);
        // 当执行userService.getUser(userId)时 返回上面定义的未激活状态User
        Mockito.when(userService.getUser(userId)).thenReturn(user);
         
        CommonResultDTO result = controller.getUserStorage(userId);
        Assertions.assertThat(result.getData()).isEqualTo(DEFAULT_INACTIVE_USER_CAPACITY);
        //验证是否执行了storageService.getUserStorage(userId);
        Mockito.verify(storageService).getUserStorage(userId);
        //验证是否执行了userService.getUser(userId);
        Mockito.verify(userService).getUser(userId);
    }
    @Test
    public void test_active_and_not_buy_user_storage(){
        //3. 验证未购买但激活用户的返回容量
        // 定义一激活状态用户对象
        String userId = "1";
        User user = new User();
        user.setStatus(ACTIVE);
        // 当执行storageService.getUserStorage(userId)时直接返回空 表示未购买
        Mockito.when(storageService.getUserStorage(userId)).thenReturn(null);
        // 当执行userService.getUser(userId)时 返回上面定义的激活状态User
        Mockito.when(userService.getUser(userId)).thenReturn(user);
         
        CommonResultDTO result = controller.getUserStorage(userId);
        Assertions.assertThat(result.getData()).isEqualTo(DEFAULT_ACTIVE_USER_CAPACITY);
        //验证是否执行了storageService.getUserStorage(userId);
        Mockito.verify(storageService).getUserStorage(userId);
        //验证是否执行了userService.getUser(userId);
        Mockito.verify(userService).getUser(userId);
    }
    @Test
    public void test_had_bought_user_storage(){
        //4. 验证已购买用户的返回容量
        String userId = "1";
        // 当执行storageService.getUserStorage(userId)时直接返回20 表示用户购买了20GB的容量
        final int CAPACITY_20 = 20;
        Mockito.when(storageService.getUserStorage(userId)).thenReturn(CAPACITY_20);
         
        CommonResultDTO result = controller.getUserStorage(userId);
        Assertions.assertThat(result.getData()).isEqualTo(CAPACITY_20);
        //验证是否执行了storageService.getUserStorage(userId);
        Mockito.verify(storageService).getUserStorage(userId);
        //用户已购买的情况下 不会执行userService.getUser(userId) 故此处设置times(0) 表示未执行
        Mockito.verify(userService,Mockito.times(0)).getUser(userId);
    }
}

无需依赖外部环境,只须通过Mockito.when().thenReturn()来准备各种待测条件下的数据(如用户不存在、激活用户、非激活用户等)。

我想若通过了上面这个逻辑严密的测试,对这段代码应该是格外有信心了吧。不用每天在胸前画十字了。

见测试结果:

补充:

Mockito maven 坐标

<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-core</artifactId>
    <version>1.9.5</version>
</dependency>


© 著作权归作者所有

共有 人打赏支持
zgw06629
粉丝 16
博文 54
码字总数 30471
作品 0
海淀
程序员
私信 提问
Kotlin 写 Android 单元测试(三),Mockito mocking 框架的使用

Kotlin 写 Android 单元测试系列: Kotlin 写 Android 单元测试(一),单元测试是什么以及为什么需要 Kotlin 写 Android 单元测试(二),JUnit 4 测试框架和 kotlin.test 库的使用 Kotlin...

JohnnyShieh
2017/12/14
0
0
【腾讯Bugly干货分享】安卓单元测试:What, Why and How

本文来自于腾讯bugly开发者社区,非经作者同意,请勿转载,原文地址:http://dev.qq.com/topic/57d28349101cd07a5404c415 Dev Club 是一个交流移动开发技术,结交朋友,扩展人脉的社群,成员...

腾讯Bugly
2016/09/26
20
0
【腾讯Bugly干货分享】安卓单元测试:What, Why and How

本文来自于腾讯bugly开发者社区,非经作者同意,请勿转载,原文地址:http://dev.qq.com/topic/57d28349101cd07a5404c415 Dev Club 是一个交流移动开发技术,结交朋友,扩展人脉的社群,成员...

腾讯Bugly
2016/10/10
488
0
Mockito入门及配合Junit进行单元测试

声明:原创文章,转载请注明出处。http://www.jianshu.com/p/46afe8c54d34 一、背景 最近一段时间在给写的框架补单元测试。发现有一些方法做单元测试不好做,主要是其内部依赖了许多外部环境...

唐影若凡
2017/11/17
0
0
自动化单元测试实践之路

info已经发布: http://www.infoq.com/cn/articles/road-of-automated-unit-testing-practices 自动化单元测试并不是什么新鲜事物,它应该是团队持之以恒的事情,可能有很多团队知道如何去做...

jooben
2014/05/12
0
0

没有更多内容

加载失败,请刷新页面

加载更多

工作中如何做好技术积累

参考:https://tech.meituan.com/study_vs_work.html 看了这篇文章,觉得总结得非常好,因此摘抄了一些关键点,以便自己经常翻阅。 引言 在繁忙的工作中做好技术积累,构建个人核心竞争力. 在...

grace_233
28分钟前
4
0
day146-2018-11-13-英语流利阅读-待学习

5 岁“牛娃”简历给 985 精英一个暴击 Lala 2018-11-13 1.今日导读 “不要让孩子输在起跑线上”,似乎已成为了当下最流行的名句,每个身为家长或还未成为家长的人都不得不思考这句话的分量。...

飞鱼说编程
41分钟前
4
0
Mariadb二进制包安装,Apache安装

安装mariadb 下载二进制包并解压 [root@test-a src]# wget https://downloads.mariadb.com/MariaDB/mariadb-10.2.6/bintar-linux-glibc_214-x86_64/mariadb-10.2.6-linux-glibc_214-x86_64.t......

野雪球
今天
4
0
ConcurrentHashMap 高并发性的实现机制

ConcurrentHashMap 的结构分析 为了更好的理解 ConcurrentHashMap 高并发的具体实现,让我们先探索它的结构模型。 ConcurrentHashMap 类中包含两个静态内部类 HashEntry 和 Segment。HashEnt...

TonyStarkSir
今天
5
0
大数据教程(7.4)HDFS的java客户端API(流处理方式)

博主上一篇博客分享了namenode和datanode的工作原理,本章节将继前面的HDFS的java客户端简单API后深度讲述HDFS流处理API。 场景:博主前面的文章介绍过HDFS上存的大文件会成不同的块存储在不...

em_aaron
昨天
4
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部