文档章节

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

zgw06629
 zgw06629
发布于 2015/04/27 12:03
字数 1064
阅读 431
收藏 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
Mockito入门及配合Junit进行单元测试

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

唐影若凡
2017/11/17
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
自动化单元测试实践之路

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

jooben
2014/05/12
0
0

没有更多内容

加载失败,请刷新页面

加载更多

Kafka技术资料总结(不断更新中)

1、Kafka实践:到底该不该把不同类型的消息放在同一个主题中 2、Kafka剖析系列: Kafka剖析(一):Kafka背景及架构介绍 Kafka设计解析(二):Kafka High Availability (上)...

九州暮云
36分钟前
1
0
面向对象设计原则(OOP)

单一职责原则(Single responsibility principle)又称单一功能原则。它规定一个类应该只有一个发生变化的原因。 核心原则:低耦合,高内聚。 一个类,应该只有一个引起它变化的原因,也就是...

gackey
今天
4
0
C++ 锁

C++提供了两种常用的锁,std::lock_guard<Lockable &T>和std::unique_lock<Lockable &T>。通常使用场景下,这两个锁用法一致。即,在构造锁对象时上锁,在析构锁对象时解锁。使用户从上锁/解...

yepanl
今天
4
0
Kali Linux Docker 練習

docker pull kalilinux/kali-linux-docker docker run -t -i kalilinux/kali-linux-docker /bin/bash apt-get update apt-get install htop apt-get install nmap apt-get install wpscan ap......

BaiyuanLab
今天
4
0
通俗大白话来理解TCP协议的三次握手和四次分手

最近在恶补计算机网络方面的知识,之前对于TCP的三次握手和四次分手也是模模糊糊,对于其中的细节更是浑然不知,最近看了很多这方面的知识,也在系统的学习计算机网络,加深自己的CS功底,就...

onedotdot
今天
4
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部