概述
一、背景
公司在用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使用问题记录一、背景二、遇到问题记录所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复