在多线程构建场景下Powermockito无法在不同类中Mock同一个静态方法

在修改单元测试的过程中,不幸踩了个坑,发现 Powermockito 的PowerMock.mockStatic(ClassThatContainsStaticMethod.class) 在多线程场景下是无法正常工作的,这再次验证了之前 ThrougthWorks 顾问说的那句话:

除非万不得已,或者是Mock遗留系统接口,否则不要使用Powermockito。

发生问题的场景是这样的 Class C 有一个静态方法,Class A 和 Class B 都需要调用这个方法完成一些功能:

Class C{truepublic static SomeObject getSomeObject(){truetrue[....]true}}Class A {trueprivate SomeObject someObject = C.getSomeObject();true[.....]}Class B {trueprivate SomeObject someObject = C.getSomeObject();true[.....]}

由于在测试中直接调用 C.getSomeObject() 会导致一些不可预期的错误,所以我想对AB类进行测试就必须使用Mock,于是我那么写:

Class ATest{true@Beforetruepublic void setUp(){truetruePowerMock.mockStatic(C.class)truetruePowerMock.when(C.C.getSomeObject()).thenReturn(PowerMock.mock(SomeObject.class))true}}Class BTest{true@Beforetruepublic void setUp(){truetruePowerMock.mockStatic(C.class)truetruePowerMock.when(C.C.getSomeObject()).thenReturn(PowerMock.mock(SomeObject.class))true}}

当我在IDE中分别运行 ATest 或者 BTest 是,我的测试都是能正确运行的,但是当你使用Maven或者其他的构建工具进行多线程测试的时候,你就会发现问题来了。一会是A抛异常,一会是B抛异常,总之就是不能很好的工作。由于我不是Powermockito的专家,所以无法深入的去探究这个问题的原因,但是我想,这应该是和静态方法本身在一个JVM内的唯一性有关,我截取了网上两个解释供参考:

Explanation 1

Without going into details let’s look at this code written using with Mockito :

given(mock.doSomethingWith(eq("A"), longThat(...)).thenReturn("C");

Which is roughly equivalent to : (* NEVER use a reference to OngoingStubbing in real test code, it might >lead to wrong test code *)

String aString = eq("A");Long aLong = longThat(...);String variableThatGiveReturnType = mock.doSomethingWith(aString, aLong);BDDOngoingStubbing<String> ongoingStubbing = given(variableThatGiveReturnType);ongoingStubbing.thenReturn("C");

The stubbing is clearly not finished until the last call thenReturn is completed, right.

Don’t you see the missing link between all those line to actually achieve the stubbing in a fluent way ? ;)

Dependening on how you do that, if you don’t synchronize this block you won’t be able to achieve any correct stubbing, otherwise concurrent access anywhere in this block will garble things in the mock internals.

And if you add the fact that the mock might be already used, with it’s own concurrent code to use the answers, you end up in with completely messed up internal states.

Anyway, always stub before using mocks concurrently.

Explanation 2

For healthy scenarios Mockito plays nicely with threads. For instance, you can run tests in parallel to speed up the build. Also, You can let multiple threads call methods on a shared mock to test in concurrent conditions. Check out a [http://mockito.googlecode.com/svn/tags/latest/javadoc/org/mockito/Mockito.html#22 timeout()] feature for testing concurrency.

However Mockito is only thread-safe in healthy tests, that is tests without multiple threads stubbing/verifying a shared mock. Stubbing or verification of a shared mock from different threads is NOT the proper way of testing because it will always lead to intermittent behavior. In general mutable state + assertions in multi-threaded environment lead to random results. If you do stub/verify a shared mock across threads you will face occasional exceptions like: WrongTypeOfReturnValue, etc.

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Java Web

JavaWeb中使用JSON

JSON 使用 JavaScript 语法来描述数据对象,但是 JSON 仍然独立于语言和平台。JSON 解析器和 JSON 库支持许多不同的编程语言。

2844
来自专栏java、Spring、技术分享

Spring MVC ControllerAdvice深入解析

  Spring 在3.2版本后面增加了一个ControllerAdvice注解。网上的资料说的都是ControllerAdvice配合ExceptionHan...

3751
来自专栏一个会写诗的程序员的博客

《Springboot极简教程》 Springboot plus Kotlin :Hello,WorldKotlin, Console: Hello,WorldSpringBoot Kotlin JP

https://github.com/MiniSpringBootTutorial/mini_springboot/blob/master/src/main/k...

764
来自专栏精讲JAVA

接口方法上的注解无法被 @Aspect 声明的切面拦截的原因分析

在Spring中使用MyBatis的Mapper接口自动生成时,用一个自定义的注解标记在Mapper接口的方法中,再利用@Aspect定义一个切面,拦截这个注解...

3232
来自专栏Hongten

原创Java版的Shell

如果你接触过windows操作系统,你应该对windows中的cmd有一定的了解。

1504
来自专栏码匠的流水账

聊聊micrometer的HistogramGauges

针对springboot应用,配备有各种export的AutoConfiguration,详见org.springframework.boot.actuate....

3282
来自专栏码匠的流水账

聊聊spring boot tomcat jdbc pool的属性绑定

本文主要研究一下spring boot tomcat jdbc pool的属性绑定

1922
来自专栏大大的微笑

JAVA中使用Jedis操作Redis

redis安装看这里:https://my.oschina.net/u/2486137/blog/1541190 需要的jar:commons-pool2 ,r...

8127
来自专栏Java Web

JavaWeb中使用JSON

3184
来自专栏菩提树下的杨过

spring mvc4的日期/数字格式化、枚举转换

日期、数字格式化显示,是web开发中的常见需求,spring mvc采用XXXFormatter来处理,先看一个最基本的单元测试:

2173

扫码关注云+社区

领取腾讯云代金券