Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SecurityException when running plain JUnit + Mockito in Eclipse RCP Project

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?

like image 333
jhyot Avatar asked Feb 18 '15 09:02

jhyot


People also ask

Can we use Mockito and JUnit?

Mockito is a java based mocking framework, used in conjunction with other testing frameworks such as JUnit and TestNG. It internally uses Java Reflection API and allows to create objects of a service.

Is Mockito and JUnit same?

JUnit is a framework that helps with writing and running your unit tests. Mockito (or any other mocking tool) is a framework that you specifically use to efficiently write certain kind of tests.

What can be mocked with Mockito?

We can use Mockito class mock() method to create a mock object of a given class or interface. This is the simplest way to mock an object. We are using JUnit 5 to write test cases in conjunction with Mockito to mock objects.

What is JUnit mocking?

What is mocking? Mocking is a process used in unit testing when the unit being tested has external dependencies. The purpose of mocking is to isolate and focus on the code being tested and not on the behavior or state of external dependencies.


1 Answers

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.

like image 51
jhyot Avatar answered Oct 10 '22 17:10

jhyot