5

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?

jhyot
  • 3,733
  • 1
  • 27
  • 44
  • Depending on which version of Mockito you use, you might be running into this [bug](https://bugs.eclipse.org/bugs/show_bug.cgi?id=349164). – Rüdiger Herrmann Feb 18 '15 at 10:35
  • @Rüdiger yes I think you are right. I found that bug as well during my research. I am now trying a workaround which I will post as an answer if it works. But I'm still glad for any kind of working solutions from other people. – jhyot Feb 18 '15 at 11:54
  • Have you checked if there is a version new of Mockito that includes the fixed CGLIB? – Rüdiger Herrmann Feb 18 '15 at 12:04
  • It seems that there is no new version yet. I've read that Mockito only uses official cglib releases, and the one with the alleged fix is not yet in an official release (although the fix is pulled into the main repository) – jhyot Feb 18 '15 at 12:14

2 Answers2

5

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.

jhyot
  • 3,733
  • 1
  • 27
  • 44
0

I ran into this same issue and was able to resolve it by using a more recent Orbit repository which pulls Mockito 2.x:

http://download.eclipse.org/tools/orbit/downloads/drops/R20181128170323/?d

This repository contains Mockito 2.23.0 which uses Byte Buddy instead of CGLIB.

In my target, I simply pull mockito-core 2.23.0 and Byte Buddy Java Agent 1.9.0 from the Orbit repository above.

<unit id="org.mockito" version="2.23.0.v20181106-1534"/>
<unit id="org.mockito.source" version="2.23.0.v20181106-1534"/>
<unit id="net.bytebuddy.byte-buddy-agent" version="1.9.0.v20181106-1534"/>
<unit id="net.bytebuddy.byte-buddy-agent.source" version="1.9.0.v20181106-1534"/>
avojak
  • 2,342
  • 2
  • 26
  • 32