java.lang.IllegalStateException: 1 matchers expected, 5 recorded.
这是一个很神奇的错误。
常规的出错是因为在mock方法里,其中某一个或者几个参数使用了EasyMock.anyxx(),而其他的使用了具体的值。
java.lang.IllegalStateException: 1 matchers expected, 5 recorded.
This exception usually occurs when matchers are mixed with raw values when recording a method:
foo(5, eq(6)); // wrong
You need to use no matcher at all or a matcher for every single param:
foo(eq(5), eq(6)); // right
foo(5, 6); // also right
at org.easymock.internal.ExpectedInvocation.createMissingMatchers(ExpectedInvocation.java:52)
at org.easymock.internal.ExpectedInvocation.<init>(ExpectedInvocation.java:41)
at org.easymock.internal.RecordState.invoke(RecordState.java:51)
at org.easymock.internal.MockInvocationHandler.invoke(MockInvocationHandler.java:40)
at org.easymock.internal.ObjectMethodsFilter.invoke(ObjectMethodsFilter.java:101)
at org.easymock.internal.ClassProxyFactory$MockMethodInterceptor.intercept(ClassProxyFactory.java:97)
at com.unit_test_easymock_mockito.database.MyDatabase$$EnhancerByCGLIB$$f0fe0b8a.updateBook(<generated>)
at com.unit_test_easymock_mockito.zexception.BookDaoImplTest.updateBookTest(BookDaoImplTest.java:85)
但本例中出现的错误并不是这个原因,附上代码:
/**
* 更新书本信息 单元测试
*/
@Test
public void updateBookTest() { Book book = new Book(); myDatabase.updateBook(EasyMock.anyObject());
EasyMock.expectLastCall(); mockControl.replay(); bookDaoImpl.updateBook(book); mockControl.verify();
}
可以看到,这里mock的myDatabase.updateBook(EasyMock.anyObject());只有一个参数,不存在一个为anyObject(),一个为具体值的情况。
并且,单独执行junit是正常通过的:

但通过maven clean install打包时,就报错了。
我们来通过debug的方式,看看错误到底出在了什么地方。



可以看到在这里报错了,错误信息也和之前报出的一致。
再仔细分析,发现是这一句代码导致的抛异常:
if (matchers.size() != invocation.getArguments().length) {
invocation.getArguments().length是调用方法的参数个数,那么matchers.size()又是什么呢,从哪里获取的呢?
继续分析代码:
public ExpectedInvocation(Invocation invocation, List<IArgumentMatcher> matchers) {
this.invocation = invocation;
this.matchers = createMissingMatchers(invocation, matchers);
}
createMissingMatchers方法是在ExpectedInvocation构造方法里调用的,再来看:
public Object invoke(Invocation invocation) {
closeMethod();
List<IArgumentMatcher> lastMatchers = LastControl.pullMatchers();
lastInvocation = new ExpectedInvocation(invocation, lastMatchers);
lastInvocationUsed = false;
return emptyReturnValueFor(invocation.getMethod().getReturnType());
}
在invoke方法里,会将matchers传递给ExpectedInvocation,而matchers是通过LastControl.pullMatchers()获取的:
public static List<IArgumentMatcher> pullMatchers() {
List<IArgumentMatcher> stack = threadToArgumentMatcherStack.get();
if (stack == null) {
return null;
}
threadToArgumentMatcherStack.remove();
return new ArrayList<>(stack);
}
这里可以看出,matchers最终是从threadToArgumentMatcherStack里获取的,并且,在获取后,会及时的threadToArgumentMatcherStack.remove();
再来了解下threadToArgumentMatcherStack是什么以及matchers是什么时候放到threadToArgumentMatcherStack里的:
private static final ThreadLocal<List<IArgumentMatcher>> threadToArgumentMatcherStack = new ThreadLocal<>();
threadToArgumentMatcherStack是一个ThreadLocal
public static void reportMatcher(IArgumentMatcher matcher) {
List<IArgumentMatcher> stack = threadToArgumentMatcherStack.get();
if (stack == null) {
stack = new ArrayList<>(5); // methods of more than 5 parameters are quite rare
threadToArgumentMatcherStack.set(stack);
}
stack.add(matcher);
}
在reportMatcher方法里,会把matchers放到threadToArgumentMatcherStack里。
那么,reportMatcher方法又是在什么时候调用的呢?


可以看出,在进行对参数的Mock的时候,anyxx(),都会调用,添加matcher。
那么问题来了,在pullMatchers()方法里,每次获取时都会及时进行清空,为什么出现“1 matchers expected, 5 recorded.”,明明只有一个参数,matchers却被添加了5次的情况?
思考分析一下,发现有一种可能性,就是调用了reportMatcher方法,但是没有调用pullMatchers()方法,这样,对于ThreadLocal类型的threadToArgumentMatcherStack,在同一个线程里,matchers会一直增加。
但对于EasyMock来说,Mock一个方法必然会调用pullMatchers()方法,那么是不是可能没有使用EasyMock来Mock方法,但却使用了EasyMock.anyObject()来Mock参数了呢?
结果果然如此:
@Test
public void queryBookByIdTest() { Integer id = 1; Mockito.when(bookDao.queryBookById(EasyMock.anyObject())).thenReturn(null); bookServiceImpl.queryBookById(id);
}
在这个单元测试里,使用了Mockito来mock方法,却同时使用了EasyMock.anyObject()来mock参数,这样就导致执行到真正EasyMock来mock方法的时候,出问题了,matchers和方法的参数对应不上,导致了问题出现。
最后还存在一个疑问:为什么单独执行单元测试不会报错,而执行clean install打包时就报出这个错呢?
个人推测执行单元测试时,是对各个单元测试方法单个执行的,即单元测试之间不会相互影响;而clean install打包时,所有的单元测试方法在同一个线程里具有相同的上下文,导致了问题出现。
所以说,在项目里写单元测试时,尽量只使用一种单元测试框架,混合使用多种单元测试框架,可能会造成很神奇的问题出现。
java.lang.IllegalStateException: 1 matchers expected, 5 recorded.的更多相关文章
- Caused by: java.lang.IllegalStateException: Expected raw type form of org.springframework.web.servlet.handler.AbstractHandlerMethodMapping$Match
spring 4.0.2,mybatis 3.2.6,aspectjweaver 1.8.10 使用的时候,报错: Caused by: java.lang.IllegalStateException ...
- 解决java.lang.IllegalStateException: The application’s PagerAdapter changed the adapter’s content
A界面中有viewpager的动态加载,从界面A跳到界面B,再finish掉B返回A时报出此异常. java.lang.IllegalStateException: The application's ...
- myeclipse 无法启动 java.lang.IllegalStateException: Unable to acquire application service. Ensure that the org.eclipse.core.runtime bundle is resolved and started (see config.ini).
把myeclipse10 按照目录完整拷贝到了另外一台电脑, 另外的目录 原安装目录 D\:\soft\i\myeclipse10 新安装目录 E\:\soft\myeclipse10 双击启动失败, ...
- java.lang.IllegalStateException:Couldn't read row 0, col -1 from CursorWindow. Make sure the Cursor is initialized correctly before accessing data from it.
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.xxx...}: java.lang.IllegalSta ...
- java.lang.IllegalStateException: Not allowed to create transaction on shared EntityManager - use Spring transactions or EJB CMT instead
java.lang.IllegalStateException: Not allowed to create transaction on sharedEntityManager - use Spri ...
- java.lang.IllegalStateException: getOutputStream() has already been called for this response
ERROR [Engine] StandardWrapperValve[jsp]: Servlet.service() for servlet jsp threw exceptionjava.lang ...
- 用java实现文件下载,提示java.lang.IllegalStateException: getOutputStream() has already been called for this response
1. 用java实现文件下载,提示java.lang.IllegalStateException: getOutputStream() has already been called for this ...
- eclipse启动报错java.lang.IllegalStateException: LifecycleProcessor not initialized - call 'refresh' befo
报错: java.lang.IllegalStateException: LifecycleProcessor not initialized - call 'refresh' before invo ...
- java.lang.IllegalStateException: Couldn't read row 1, col 0 from CursorWindow. Make sure the Cursor is initialized correctly before accessing data fr
Android中操作Sqlite遇到的错误:java.lang.IllegalStateException: Couldn't read row 1, col 0 from CursorWindow. ...
随机推荐
- IDA*算法——骑士精神
例题 骑士精神 Description 在一个5×5的棋盘上有12个白色的骑士和12个黑色的骑士, 且有一个空位.在任何时候一个骑士都能按照骑士的走法(它可以走到和它横坐标相差为1,纵坐标相差为2或者 ...
- 廖雪峰Java14Java操作XML和JSON-2JSON-2处理JSON
解析JSON JSR 353 API 常用的第三方库 * Jackson * gson * fastjson Jackson: 提供了读写JSON的API JSON和JavaBean可以互相转换 可食 ...
- 完全卸载之前8.0的Mysql,安装5.5mysql
完全卸载: https://blog.csdn.net/sxingming/article/details/52601250 安装mysql5.5: https://blog.csdn.net/fly ...
- django-rest-framework-jwt的使用
一简介 JWT 是一个开放标准(RFC 7519),它定义了一种用于简洁,自包含的用于通信双方之间以 JSON 对象的形式安全传递信息的方法.该信息可以被验证和信任,因为它是数字签名的.JWTS可以使 ...
- CobaltStrike与Metasploit实战联动
前言 CobalStrike 与 Metasploit 均是渗透利器,各有所长.前者更适合做稳控平台,后者则更擅长内网各类探测搜集与漏洞利用.两者更需要灵活的联动,各自相互依托,从而提升渗透的效率. ...
- iOS之UIButton的normal和selected状态切换
1.问题? 改变播放器按钮状态,发现从selected切回到normal状态时候,中间会切换normal-selected-normal的图片? 2.原因…… normal-->selected ...
- iOS程序两中启动图方式和一些坑LaunchImage 和 Assets.xcassets(Images.xcassets)
一.通过LaunchScreen.storyboard 作启动图 1>在LaunchScreen.storyboard中拖拽一个imageView放上启动图片 注意:记得勾选右边的 User a ...
- 按钮事件v-on
<!DOCTYPE html> <html lang="zh"> <head> <title></title> < ...
- 2019-8-31-dotnet-通过-WMI-获取系统安装的驱动
title author date CreateTime categories dotnet 通过 WMI 获取系统安装的驱动 lindexi 2019-08-31 16:55:59 +0800 20 ...
- python3 新特性
1.格式化字符串f-string user = "Mike" log_message = f'User{user} has logged in' 2.路径管理库Pathlib 3. ...