I have an Eclipse RCP Project with multiple plugins. I am writing plain JUnit tests (no dependencies to Eclipse/UI) as separate fragments to the plugin-under-test.
When using Mockito and trying to mock an interface from another plugin (which is exported correctly; I can use the interface in my code), I get a SecurityException related to class signing:
org.mockito.exceptions.base.MockitoException:
Mockito cannot mock this class: interface ch.sbb.polar.client.communication.inf.service.IUserService
Mockito can only mock visible & non-final classes.
If you're not sure why you're getting this error, please report to the mailing list.
at org.mockito.internal.runners.JUnit45AndHigherRunnerImpl$1.withBefores(JUnit45AndHigherRunnerImpl.java:27)
[...]
Caused by: org.mockito.cglib.core.CodeGenerationException: java.lang.reflect.InvocationTargetException-->null
at org.mockito.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:238)
[...]
Caused by: java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
[...]
Caused by: java.lang.SecurityException: Signers of 'ch.sbb.polar.client.communication.inf.service.IUserService$$EnhancerByMockitoWithCGLIB$$a8bfe723' do not match signers of other classes in package
at java.lang.ClassLoader.checkPackageSigners(ClassLoader.java:361)
at java.lang.ClassLoader.defineClass(ClassLoader.java:295)
... 40 more
When I run the tests as "JUnit Plugin tests", i.e. with an OSGi environment, everything works as expected. But I'd like to use the plain JUnit execution because of speed; in the class under test, I don't need the OSGi environment.
Does anybody know a way to do that?
As is mentioned in the comments, the root cause is that the Eclipse Orbit package of Mockito (which I had added to my target platform) is signed, and because of a bug in the underlying CGLIB, you cannot mock unsigned classes/interfaces with a signed Mockito.
See https://code.google.com/p/mockito/issues/detail?id=393 for the most detailed description. The bug is fixed in CGLIB head, but has not yet appeared in a release. Mockito only uses released versions as dependencies, so the fix is not yet in Mockito, with an unknown (to me) timeline, as when this will be in.
The workaround is to package the Mockito JAR (and its dependencies) in its own bundle and export the necessary API packages.
When using Maven Tycho, JUnit, Hamcrest, and Mockito, the only way I was able to make this work and resolve all dependency / classpath / classloader issues correctly was the following way:
Create Maven module with the following entries in the pom.xml:
<packaging>eclipse-plugin</packaging>
[...]
<dependencies>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>1.10.19</version>
</dependency>
</dependencies>
[...]
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>copy-test-libs</id>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<outputDirectory>lib</outputDirectory>
<stripVersion>true</stripVersion>
<includeScope>runtime</includeScope>
</configuration>
</execution>
</executions>
</plugin>
Use following entries in the MANIFEST.MF:
Bundle-ClassPath: lib/mockito-core.jar,
lib/objenesis.jar
Export-Package: org.mockito,
org.mockito.runners
Require-Bundle: org.junit;bundle-version="4.11.0";visibility:=reexport,
org.hamcrest.library;bundle-version="1.3.0";visibility:=reexport,
org.hamcrest.core;bundle-version="1.3.0";visibility:=reexport
And finally in your unit test fragment, add this new bundle as a dependency.