我是靠谱客的博主 自觉小馒头,最近开发中收集的这篇文章主要介绍Mockito使用问题记录一、背景二、遇到问题记录,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

一、背景

公司在用Mockito进行单元测试,写单测的时候总会遇到一些小问题,顺手记录下来。

二、遇到问题记录

2.1 mock update 报错

2.1.1 问题现象

当update使用如下写法时,mock会报错
被mock代码示例:
在这里插入图片描述
mock语句:

when(platformMallOrderServcie.update(any())).thenReturn(true);

报错:

ERROR com.longfor.gf.lw.ordercenter.business.OrderBusiness - callbackMerchant回调异常,原因:cannot find column’s cache for “com.longfor.gf.lw.ordercenter.entity.PlatformMallOrder”, so you cannot used “class com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper”!
com.baomidou.mybatisplus.core.exceptions.MybatisPlusException: cannot find column’s cache for “com.longfor.gf.lw.ordercenter.entity.PlatformMallOrder”, so you cannot used “class com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper”!

2.1.2 问题原因

根据测试,发现只有update时使用lambda进行set操作会有问题。推测是mock框架和mybatis-plus框架有冲突。

2.1.3 解决方案

解决方法有两个,两种方案均可实现mock不报错。

第一种解决方案:修改update写法
不使用lambda即可。修改后代码如下:
在这里插入图片描述
第二种解决方案:修改mock方法
除非mock时发现代码有问题,一般情况下,不会因为mock修改代码,那么就要修改mock方法。报错中提示是目标类没有加载进mybatis-plus的缓存中,那么mock时手动将其加载进缓存即可。代码如下:

根据版本号选择
TableInfoHelper.initTableInfo(null, PlatformMallOrder.class);TableInfoHelper.initTableInfo(new MapperBuilderAssistant(new MybatisConfiguration(), ""), RefundTradeProcessPool.class);

改后如下:

TableInfoHelper.initTableInfo(null, PlatformMallOrder.class);
when(platformMallOrderServcie.update(any())).thenReturn(true);

注:如果有其他方案,欢迎大家补充。

2.2 mock mybatis-plus生成的service报错

2.2.1 问题现象

以PlatformMallGoodsServiceImpl类为例。此类定义如下

public class PlatformMallGoodsServiceImpl extends ServiceImpl<PlatformMallGoodsMapper, PlatformMallGoods> implements IPlatformMallGoodsService

所以应该mock对应的mapper方法

when(platformMallGoodsMapper.update(any(PlatformMallGoods.claaa),any(UpdateWrapper.class))).thenReturn(1);

代码中有如下写法,mock时会报空指针

this.update(goodsUpdateWrapper)

2.2.2 查找问题点

通过debug可以找到对应的报错代码
在这里插入图片描述
发现是mapper没有注进去。

2.2.3 问题原因

原因是使用的注解不同

// mock目标类的注解
@InjectMocks
private PlatformMallGoodsServiceImpl platformMallGoodsService;
// 其他被mock的类
@Mock
private ISecondhandGoodsDetailService secondhandGoodsDetailService;

@InjectMocks标记的类会被实例化,@Mock标记的类会被注入到@InjectMocks标记的类中。

2.2.4 解决方案

mock方法中手动注入mapper即可,代码写法如下

@Mock
private PlatformMallGoodsMapper platformMallGoodsMapper;
// 单测方法中增加mapper注入
ReflectionTestUtils.setField({@InjectMocks标记的类}, "baseMapper", {@InjectMocks标记的类对应的mapper});

按照上述写法,此例中添加的mock代码如下

@Mock
private PlatformMallGoodsMapper platformMallGoodsMapper;
// 单测方法中增加mapper注入
ReflectionTestUtils.setField(platformMallGoodsService, "baseMapper", platformMallGoodsMapper);

2.3 mock List失败

2.3.1 问题现象

@Mock一个List变量不生效,代码如下

@Mock
private List<TimeFormatStrategy> mockStrategies;

在真正运行时会报NPE,因为strategies为null,报错代码如下

for (TimeFormatStrategy strategy : strategies) 

2.3.2 问题原因

具体原因没有找到,我观察到在单测方法中list会被mock出来,但走到真正的方法内list就变成null了,仿佛mock没有生效。

参见:如何mock一个List类型的属性

2.3.3 解决方案

将@Mock换成@Spy注解,然后增加一个赋值逻辑即可

@Spy
private List<TimeFormatStrategy> mockStrategies = new ArrayList<>();
// 不用一定放到Before里,放在对应的Test方法中也可以,我是懒得一个一个放就这么写了
@Before
public void init() {
    mockStrategies.add(mockDefaultTimeFormatStrategy);
}

2.4 mock 抽象类

2.4.1 问题现象

@InjectMocks 抽象类报错,代码如下

// 抽象类定义
public abstract class AbstractDiscountJob{
	// 中间内容省略
}

// 单测中的写法
@InjectMocks
private AbstractDiscountJob abstractDiscountJobUnderTest;

报错如下

org.mockito.exceptions.base.MockitoException: 
Cannot instantiate @InjectMocks field named 'abstractDiscountJobUnderTest'.
You haven't provided the instance at field declaration so I tried to construct the instance.
However, I failed because: the type 'AbstractDiscountJob is an abstract class.
Examples of correct usage of @InjectMocks:
   @InjectMocks Service service = new Service();
   @InjectMocks Service service;
   //also, don't forget about MockitoAnnotations.initMocks();
   //and... don't forget about some @Mocks for injection :)
	at org.mockito.internal.runners.JUnit45AndHigherRunnerImpl$1.withBefores(JUnit45AndHigherRunnerImpl.java:27)
	at org.junit.runners.BlockJUnit4ClassRunner.methodBlock(BlockJUnit4ClassRunner.java:276)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
	at org.mockito.internal.runners.JUnit45AndHigherRunnerImpl.run(JUnit45AndHigherRunnerImpl.java:37)
	at org.mockito.runners.MockitoJUnitRunner.run(MockitoJUnitRunner.java:62)
	at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
	at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69)
	at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)
	at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:220)
	at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:53)
Caused by: org.mockito.exceptions.base.MockitoException: the type 'AbstractDiscountJob is an abstract class.
	... 17 more

2.4.2 问题原因

@InjectMocks 可以创建一个能够访问的实例,但抽象类不能被实例化。
报错信息的开头部分已经写明原因

Cannot instantiate @InjectMocks field named ‘abstractDiscountJobUnderTest’.
You haven’t provided the instance at field declaration so I tried to construct the instance.
However, I failed because: the type 'AbstractDiscountJob is an abstract class.

2.4.3 解决方案

报错信息开头部分往后看,作者也提示了解决办法

Examples of correct usage of @InjectMocks:
@InjectMocks Service service = new Service();
@InjectMocks Service service;
//also, don’t forget about MockitoAnnotations.initMocks();
//and… don’t forget about some @Mocks for injection ????

改成下方写法即可

@InjectMocks
private AbstractDiscountJob abstractDiscountJobUnderTest = mock(AbstractDiscountJob.class, Mockito.CALLS_REAL_METHODS);

2.4.4 采坑记录

虽然报错信息说了解决方法,然而我当时并没有仔细看,直接去冲浪搜解决办法了 =_=|||
网上针对mock抽象类有两个问题,一个是如何 InjectMocks 一个抽象类,就是我遇到的问题,另一个是InjectMocks的实例中引用了抽象类,要mock这个抽象类。
这两个问题容易混淆,有的作者写的不清楚,需要看完全文才知道解决的是哪个问题。
网上的方法的确可以实例化抽象类,也能进到方法里,但无法将单测中依赖的@Mock参数带进去,所以改成了现在的这种写法。
本文参考了以下博客,在此感谢所有作者以及积极回答问题的同学

参考链接:
Mockito关于抽象类的问题
使用Mockito测试抽象类

最后

以上就是自觉小馒头为你收集整理的Mockito使用问题记录一、背景二、遇到问题记录的全部内容,希望文章能够帮你解决Mockito使用问题记录一、背景二、遇到问题记录所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(37)

评论列表共有 0 条评论

立即
投稿
返回
顶部