SecurityException when running plain JUnit + Mockito in Eclipse RCP Project

eclipse-plugin, eclipse-rcp, junit, mockito, osgi-fragment

Solution

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.

Workaround: Provide unsigned Mockito in separate bundle

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.

Problem

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?

Original source