【Java】CompletableFuture+Mockito单元测试不通过 Unnecessary stubbings detected

问题描述

有个接口使用CompletableFuture实现的异步调用,现在要用Mockito写单元测试

	@Test
    public void updateNumAsync() {
        Integer newNum = 600;
        // updateRoleCountAsync用CompletableFuture异步调用的ApiUtil.put发送http请求更新对方服务端的数据
        // 生成要用的stub
        when(ApiUtil.put(Constants.UPDATE_COUNT, newNum.toString(), serverId)).thenReturn("{\"code\":0}");
        App.updateNumAsync(serverId, newNum).whenComplete((result, throwable) -> {
            assertEquals(result.getCode(), 0);
        });
    }

结果测试不通过:

Tests Failed: 1 of 1 test
Unnecessary stubbings detected.
Clean & maintainable test code requires zero unnecessary code.

问题分析

看控制台输出的意思大概就是when(...).return(...) mock的stub没被用到,然后测试不通过。
因为测试过程主要就是:1)mock一个要用的stub; 2)调用待测接口;3)检查结果。由于这里是异步调用,updateNumAsync里调用的CompletableFuture.supplyAsync()用的ForkJoinPool,会有一个线程1在后台异步执行updateNum的操作,因此猜测可能是当前test的线程0在异步过程中先结束了,导致线程0 Mock的stub并没有被线程1执行的待测试接口用到,导致Tests Failed

解决

Thread.sleep

既然Test的线程0结束的太早,那么强行让他多等一会是不是就好了?

	@Test
    public void updateNumAsync() throws InterruptedException {
        Integer newNum = 600;
        // updateRoleCountAsync用CompletableFuture异步调用的ApiUtil.put发送http请求更新对方服务端的数据
        // 生成要用的stub
        when(ApiUtil.put(Constants.UPDATE_COUNT, newNum.toString(), serverId)).thenReturn("{\"code\":0}");
        App.updateNumAsync(serverId, newNum).whenComplete((result, throwable) -> {
            assertEquals(result.getCode(), 0);
        });
        Thread.sleep(1000L);
    }

结果测试通过,证明之前的猜想应该是对的。但不太推荐这样做。

Tests Passed: 1 of 1 test

get()

CompletableFuture通过get()获取异步调用结果时,会阻塞当前线程直到异步操作结束返回。也就是说test的线程0不会提早结束,导致虚拟机栈中的stub在被线程1 调用之前被回收。

	@Test
    public void updateNumAsync() throws  InterruptedException, ExecutionException {
        Integer newNum = 600;
        // updateRoleCountAsync用CompletableFuture异步调用的ApiUtil.put发送http请求更新对方服务端的数据
        // 生成要用的stub
        when(ApiUtil.put(Constants.UPDATE_COUNT, newNum.toString(), serverId)).thenReturn("{\"code\":0}");
        App.updateNumAsync(serverId, newNum).whenComplete((result, throwable) -> {
            assertEquals(result.getCode(), 0);
        }).get();
    }

结果测试通过.

Tests Passed: 1 of 1 test

Mockito.lenient()

stackoverflow上面有人在mock多个stub的同时(用了get()),但也还会出现Unnecessary stubbings detected.,详情可以看原帖。大概就是有时Mockito可能没有按照确定的顺序调用这些方法,此时就可以用lenient()
这个方法在前面那个问题里也是能让测试通过的。

	@Test
    public void updateNumAsync(){
        Integer newNum = 600;
        // updateRoleCountAsync用CompletableFuture异步调用的ApiUtil.put发送http请求更新对方服务端的数据
        // 生成要用的stub
        Mockito.lenient().when(ApiUtil.put(Constants.UPDATE_COUNT, newNum.toString(), serverId)).thenReturn("{\"code\":0}");
        App.updateNumAsync(serverId, newNum).whenComplete((result, throwable) -> {
            assertEquals(result.getCode(), 0);
        });
    }

具体原理还不是很明白,反正就是能work,先埋个坑,之后有空再看看⑧

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值