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

许多同事怕写单元测试的一个主要原因(或借口)就是依赖太多(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简单介绍

补充:

Mockito maven 坐标

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


你可能感兴趣的:(mockito)