Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mockito 2.0.94-beta breaks org.springframework.boot.test.mock.mockito.MockReset #6520

Closed
thetric opened this issue Jul 31, 2016 · 18 comments
Closed
Assignees
Labels
type: enhancement A general enhancement
Milestone

Comments

@thetric
Copy link

thetric commented Jul 31, 2016

Due to a breaking change in Mockito's internal API it is not possible to execute tests and an IllegalAccessError is thrown (see below)

Formerly Mockito's MockUtils had a public constructor but with this change the class becomes a utility class with no directly accessible constructor. (see mockito/mockito@3fe3b62#diff-321079f7242b016035f4577222dfe7a3R26)
This change breaks org.springframework.boot.test.mock.mockito.MockReset (see https://github.com/spring-projects/spring-boot/blob/master/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/MockReset.java#L56)

Tested with Spring Boot 1.4.0.RELEASE and mockito-core 2.0.94-beta

2016-07-31 15:39:57.810  WARN 12128 --- [    Test worker] o.s.test.context.TestContextManager      : Caught exception while allowing TestExecutionListener [org.springframework.boot.test.mock.mockito.ResetMocksTestExecutionListener@32d199f2] to process 'before' execution of test method [public void de.adesso.adlacarte.springcloud.auth.ExampleControllerTest.testExample() throws java.lang.Exception] for test instance [de.adesso.adlacarte.springcloud.auth.ExampleControllerTest@3b984f0f]

java.lang.IllegalAccessError: tried to access method org.mockito.internal.util.MockUtil.<init>()V from class org.springframework.boot.test.mock.mockito.MockReset
    at org.springframework.boot.test.mock.mockito.MockReset.<clinit>(MockReset.java:56) ~[spring-boot-test-1.4.0.RELEASE.jar:1.4.0.RELEASE]
    at org.springframework.boot.test.mock.mockito.ResetMocksTestExecutionListener.beforeTestMethod(ResetMocksTestExecutionListener.java:44) ~[spring-boot-test-1.4.0.RELEASE.jar:1.4.0.RELEASE]
    at org.springframework.test.context.TestContextManager.beforeTestMethod(TestContextManager.java:269) ~[spring-test-4.3.2.RELEASE.jar:4.3.2.RELEASE]
    at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:74) [spring-test-4.3.2.RELEASE.jar:4.3.2.RELEASE]
    at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86) [spring-test-4.3.2.RELEASE.jar:4.3.2.RELEASE]
    at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84) [spring-test-4.3.2.RELEASE.jar:4.3.2.RELEASE]
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325) [junit-4.12.jar:4.12]
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:252) [spring-test-4.3.2.RELEASE.jar:4.3.2.RELEASE]
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:94) [spring-test-4.3.2.RELEASE.jar:4.3.2.RELEASE]
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290) [junit-4.12.jar:4.12]
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71) [junit-4.12.jar:4.12]
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288) [junit-4.12.jar:4.12]
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58) [junit-4.12.jar:4.12]
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268) [junit-4.12.jar:4.12]
    at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61) [spring-test-4.3.2.RELEASE.jar:4.3.2.RELEASE]
    at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70) [spring-test-4.3.2.RELEASE.jar:4.3.2.RELEASE]
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363) [junit-4.12.jar:4.12]
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:191) [spring-test-4.3.2.RELEASE.jar:4.3.2.RELEASE]
    at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecuter.runTestClass(JUnitTestClassExecuter.java:114) [gradle-testing-jvm-2.14.1.jar:2.14.1]
    at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecuter.execute(JUnitTestClassExecuter.java:57) [gradle-testing-jvm-2.14.1.jar:2.14.1]
    at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassProcessor.processTestClass(JUnitTestClassProcessor.java:66) [gradle-testing-jvm-2.14.1.jar:2.14.1]
    at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.processTestClass(SuiteTestClassProcessor.java:51) [gradle-testing-base-2.14.1.jar:2.14.1]
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_102]
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_102]
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_102]
    at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_102]
    at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:35) [gradle-messaging-2.14.1.jar:2.14.1]
    at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24) [gradle-messaging-2.14.1.jar:2.14.1]
    at org.gradle.internal.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:32) [gradle-messaging-2.14.1.jar:2.14.1]
    at org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:93) [gradle-messaging-2.14.1.jar:2.14.1]
    at com.sun.proxy.$Proxy2.processTestClass(Unknown Source) [na:na]
    at org.gradle.api.internal.tasks.testing.worker.TestWorker.processTestClass(TestWorker.java:109) [gradle-testing-base-2.14.1.jar:2.14.1]
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_102]
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_102]
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_102]
    at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_102]
    at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:35) [gradle-messaging-2.14.1.jar:2.14.1]
    at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24) [gradle-messaging-2.14.1.jar:2.14.1]
    at org.gradle.internal.remote.internal.hub.MessageHub$Handler.run(MessageHub.java:377) [gradle-messaging-2.14.1.jar:2.14.1]
    at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:54) [gradle-base-services-2.14.1.jar:2.14.1]
    at org.gradle.internal.concurrent.StoppableExecutorImpl$1.run(StoppableExecutorImpl.java:40) [gradle-base-services-2.14.1.jar:2.14.1]
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) [na:1.8.0_102]
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) [na:1.8.0_102]
    at java.lang.Thread.run(Thread.java:745) [na:1.8.0_102]

2016-07-31 15:39:57.947  WARN 12128 --- [    Test worker] o.s.test.context.TestContextManager      : Caught exception while allowing TestExecutionListener [org.springframework.boot.test.mock.mockito.ResetMocksTestExecutionListener@32d199f2] to process 'after' execution for test: method [public void de.adesso.adlacarte.springcloud.auth.ExampleControllerTest.testExample() throws java.lang.Exception], instance [de.adesso.adlacarte.springcloud.auth.ExampleControllerTest@3b984f0f], exception [java.lang.IllegalAccessError: tried to access method org.mockito.internal.util.MockUtil.<init>()V from class org.springframework.boot.test.mock.mockito.MockReset]

java.lang.NoClassDefFoundError: Could not initialize class org.springframework.boot.test.mock.mockito.MockReset
    at org.springframework.boot.test.mock.mockito.ResetMocksTestExecutionListener.afterTestMethod(ResetMocksTestExecutionListener.java:49) ~[spring-boot-test-1.4.0.RELEASE.jar:1.4.0.RELEASE]
    at org.springframework.test.context.TestContextManager.afterTestMethod(TestContextManager.java:319) ~[spring-test-4.3.2.RELEASE.jar:4.3.2.RELEASE]
    at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:94) [spring-test-4.3.2.RELEASE.jar:4.3.2.RELEASE]
    at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84) [spring-test-4.3.2.RELEASE.jar:4.3.2.RELEASE]
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325) [junit-4.12.jar:4.12]
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:252) [spring-test-4.3.2.RELEASE.jar:4.3.2.RELEASE]
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:94) [spring-test-4.3.2.RELEASE.jar:4.3.2.RELEASE]
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290) [junit-4.12.jar:4.12]
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71) [junit-4.12.jar:4.12]
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288) [junit-4.12.jar:4.12]
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58) [junit-4.12.jar:4.12]
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268) [junit-4.12.jar:4.12]
    at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61) [spring-test-4.3.2.RELEASE.jar:4.3.2.RELEASE]
    at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70) [spring-test-4.3.2.RELEASE.jar:4.3.2.RELEASE]
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363) [junit-4.12.jar:4.12]
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:191) [spring-test-4.3.2.RELEASE.jar:4.3.2.RELEASE]
    at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecuter.runTestClass(JUnitTestClassExecuter.java:114) [gradle-testing-jvm-2.14.1.jar:2.14.1]
    at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecuter.execute(JUnitTestClassExecuter.java:57) [gradle-testing-jvm-2.14.1.jar:2.14.1]
    at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassProcessor.processTestClass(JUnitTestClassProcessor.java:66) [gradle-testing-jvm-2.14.1.jar:2.14.1]
    at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.processTestClass(SuiteTestClassProcessor.java:51) [gradle-testing-base-2.14.1.jar:2.14.1]
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_102]
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_102]
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_102]
    at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_102]
    at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:35) [gradle-messaging-2.14.1.jar:2.14.1]
    at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24) [gradle-messaging-2.14.1.jar:2.14.1]
    at org.gradle.internal.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:32) [gradle-messaging-2.14.1.jar:2.14.1]
    at org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:93) [gradle-messaging-2.14.1.jar:2.14.1]
    at com.sun.proxy.$Proxy2.processTestClass(Unknown Source) [na:na]
    at org.gradle.api.internal.tasks.testing.worker.TestWorker.processTestClass(TestWorker.java:109) [gradle-testing-base-2.14.1.jar:2.14.1]
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_102]
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_102]
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_102]
    at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_102]
    at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:35) [gradle-messaging-2.14.1.jar:2.14.1]
    at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24) [gradle-messaging-2.14.1.jar:2.14.1]
    at org.gradle.internal.remote.internal.hub.MessageHub$Handler.run(MessageHub.java:377) [gradle-messaging-2.14.1.jar:2.14.1]
    at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:54) [gradle-base-services-2.14.1.jar:2.14.1]
    at org.gradle.internal.concurrent.StoppableExecutorImpl$1.run(StoppableExecutorImpl.java:40) [gradle-base-services-2.14.1.jar:2.14.1]
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) [na:1.8.0_102]
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) [na:1.8.0_102]
    at java.lang.Thread.run(Thread.java:745) [na:1.8.0_102]
@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged label Jul 31, 2016
@thetric thetric changed the title Mockito 2.0.94-beta breaks org.springframework.boot.test.mock.mockito.MockReset [spring-boot-test] Mockito 2.0.94-beta breaks org.springframework.boot.test.mock.mockito.MockReset Jul 31, 2016
@snicoll snicoll changed the title [spring-boot-test] Mockito 2.0.94-beta breaks org.springframework.boot.test.mock.mockito.MockReset Mockito 2.0.94-beta breaks org.springframework.boot.test.mock.mockito.MockReset Jul 31, 2016
@philwebb
Copy link
Member

We'll need to switch to reflection calls I guess.

@philwebb philwebb added type: enhancement A general enhancement and removed status: waiting-for-triage An issue we've not yet triaged labels Jul 31, 2016
@philwebb philwebb added this to the 1.4.1 milestone Jul 31, 2016
@philwebb
Copy link
Member

Marking as an enhancement because Mockto 2 isn't officially supported yet.

@TimvdLippe
Copy link

Hey, Mockito developer here. Just found out this usage of MockUtil. We would like to discuss possible improvements of our public API to prevent usage of our internal API. Recently we have introduced a listener which can be observed by frameworks to receive some notifications, we might be able to expand this to other usecases as well. Please let us know what we can do :)

@philwebb
Copy link
Member

philwebb commented Aug 9, 2016

Hi @TimvdLippe, thanks for the offer of help!

We currently use MockUtil in a few places. This specific stack trace is related to a trick we use in org.springframework.boot.test.mock.mockito.MockReset. We basically need to offer different reset strategies when a mock is created that we can actually apply later. To do this we attach a dummy InvocationListener when the mock is created, then later on use MockUtil to get it back again. If there were a different way to attach arbitrary meta-data to a mock instance, that would be very helpful.

@philwebb
Copy link
Member

philwebb commented Aug 9, 2016

@TimvdLippe The other places we use it are:

@TimvdLippe
Copy link

@philwebb The mock meta-data seems like an interesting usecase! Are you willing to create an issue on https://github.com/mockito/mockito with a proposed API to make this as easy as possible for a user. We can use that as a starting point to investigate how we can integrate this into the current framework 🎉

Regarding the SpyDefinition, that should not be broken with the static method, only the constructor of MockUtil. Does not really seem like a hack, so I am fine with it.

Lastly regarding Spring AOP: I am sorry but I have no experience with Spring or any of its proxies. @marcingrzejszczak are you familiar with this code and maybe know what is going on?

@philwebb
Copy link
Member

philwebb commented Aug 9, 2016

@TimvdLippe sure thing. See mockito/mockito#539

@marcingrzejszczak
Copy link
Contributor

@TimvdLippe @philwebb yeah I can check it out tomorrow

@philwebb
Copy link
Member

philwebb commented Aug 9, 2016

I think ideally we'd need a first class way to tell if mocking has started and optionally switch out the target. The problem we have is that Spring AOP provides its own proxy which can get in the way.

So for example, say you're using @Cachable, this will create a cache aspect to take care of either calling the target method or returning a previously cached result. When using Mockito result is something like this:

+-AOP Proxy----------+
|                    |
|  +-Mock Proxy---+  |
|  |              |  | 
|  |  +--------+  |  |
|  |  | Target |  |  |
|  |  +--------+  |  |
|  |              |  |
|  +--------------+  |
|                    |
+--------------------+

If two calls are made to a @Cachable method the second one never reaches the target:

[ AOP ]     [ Mock ]     [ Target ]
   |            |            |
-->|----------->|----------->|
<--|<-----------|<-----------|
   |            |            |
-->|            |            |
<--|            |            |

Usually this is fine, but in the case of a verify(...) call it's critical that all calls make it. To do that we effectively add another aspect to bypass Spring AOP entirely and change the mock used by MockAwareVerificationMode to be the real target and not the AOP proxy.

@mhworth
Copy link

mhworth commented Sep 16, 2016

Is there a workaround for this at the moment? Perhaps a way to disable the automated mock reseting? I'm stuck at the moment between this bug and mockito/mockito#72 . I'm trying to do integration testing in Groovy using injected mocks--see http://stackoverflow.com/questions/39485118/mocking-a-groovy-service-with-spring-boot for a more complete description of the problem I'm running into.

@wilkinsona
Copy link
Member

@mhworth We don't officially support Mockito 2.0 yet. If you need to use it so that you can use Groovy, then I think your best bet at the moment is to not use @MockBean.

@agoston
Copy link

agoston commented Nov 2, 2016

Is there an ETA on this one? It's a shame we can't use the java8 lambda features present in mockito2.

@wilkinsona
Copy link
Member

@agoston No precise ETA. The issue's assigned to 1.5.0.RC1.

@rinoto
Copy link

rinoto commented Nov 2, 2016

+1

1 similar comment
@qwert321
Copy link

+1

@wilkinsona wilkinsona modified the milestones: 1.5.0 RC1, 1.5.0 M1 Nov 17, 2016
@wilkinsona wilkinsona self-assigned this Nov 18, 2016
@dam5s
Copy link

dam5s commented Dec 17, 2016

How does one disable the MockReset feature? It's crashing my test suite while I never use any MockBean (but I am using the latest version of Mockito). Am I missing something?

@dam5s
Copy link

dam5s commented Dec 18, 2016

Replying to my own question:

The @TestExecutionListeners annotation on the test class can list the listeners to use.

Out of my debugger, the list built by default is the following:

// org.springframework.boot.test.mock.mockito.MockitoTestExecutionListener
// org.springframework.boot.test.mock.mockito.ResetMocksTestExecutionListener
// org.springframework.boot.test.autoconfigure.restdocs.RestDocsTestExecutionListener
// org.springframework.boot.test.autoconfigure.web.client.MockRestServiceServerResetTestExecutionListener
// org.springframework.boot.test.autoconfigure.web.servlet.MockMvcPrintOnlyOnFailureTestExecutionListener
// org.springframework.boot.test.autoconfigure.web.servlet.WebDriverTestExecutionListener
// org.springframework.test.context.web.ServletTestExecutionListener
// org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener
// org.springframework.boot.test.autoconfigure.SpringBootDependencyInjectionTestExecutionListener
// org.springframework.test.context.support.DirtiesContextTestExecutionListener
// org.springframework.test.context.transaction.TransactionalTestExecutionListener
// org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener

My tests didn't actually need any, so I annotated my test with the following (in Kotlin):

@TestExecutionListeners(listeners = arrayOf())

@jukecraft
Copy link

@dam5s: I found a workaround without having to remove all execution listeners: #2572 (comment)

mneverov added a commit to mneverov/KMS that referenced this issue Jan 24, 2017
spring-boot-starter-test already contains Junit and Mockito and allows not to use @ContextConfiguration(classes = arrayOf(Kms::class) in each test. When using certain version of Mockito got an error (spring-projects/spring-boot#6520).
@luy07
Copy link

luy07 commented Nov 21, 2017

I found the workaround after some tries base on comment of @dam5s
I add the code like this:
@TestExecutionListeners(listeners = {SpringBootDependencyInjectionTestExecutionListener.class, ServletTestExecutionListener.class})

spring.boot version:1.4.2.RELEASE
org.mockito version:2.12.0

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: enhancement A general enhancement
Projects
None yet
Development

No branches or pull requests