IllegalClassFormatException when using JaCoCo with JMockit

1,566 views
Skip to first unread message

Jim Anderson

unread,
Sep 20, 2012, 1:20:02 PM9/20/12
to jac...@googlegroups.com
I'm getting an IllegalClassFormatException when running tests with jacoco, when the class under test creates an instance of a class that is mocked by the test.  Here's a simple example:

public class Counter {
    
    // Counter2 will be mocked by the test!
    private static Counter2 counter2;
    
    static {        
        System.out.println("in static initializer of Counter!");
        counter2 = new Counter2(); 
    }
    
    public Integer addOne(Integer input) {
        System.out.println("in Counter.addOne");
        return ++input;
    }
    
    public Integer addTwo(Integer input) {
        return input + 2;
    }
}


public class Counter2 {

    public Integer addFive(Integer num) {
        return  num + 5;
    }

}


public class TestCounter {
    
    @Before
    public void setUp() {
        Mockit.setUpMock(MockCounter.class);
        Mockit.setUpMock(MockCounter2.class);
        System.out.println("in TestCounter.setup");
    }
    
    @After
    public void tearDown() {
        System.out.println("in TestCounter.teardown");
        Mockit.tearDownMocks();
    }
    
    @Test
    public void testAdd() {
        System.out.println("in TestCounter.testAdd");
        Counter c = new Counter();
        assertEquals((Integer) 3, c.addOne(2));
    }
    
    @MockClass(realClass = Counter.class)
    public static class MockCounter {
    
        public static Integer num = new Integer(1);
                
        @Mock
        public Integer addTwo(Integer input) {
            System.out.println("in mocked addTwo!!!!!");
            return new Integer(1);
        }
    }
    
    @MockClass(realClass = Counter2.class) 
    public static class MockCounter2 {
     
        @Mock
        public Integer addFive(Integer num) { 
            System.out.println("in MockCounter2.addFive");
            return new Integer(5);
        }
    }
}

This is the output including the stack trace:

[jacoco:coverage] Enhancing junit with coverage
    [junit] Testsuite: TestCounter
    [junit] Tests run: 1, Failures: 0, Errors: 0, Time elapsed: 0.716 sec
    [junit]
    [junit] ------------- Standard Output ---------------
    [junit] in static initializer of Counter!
    [junit] in TestCounter.setup
    [junit] in TestCounter.testAdd
    [junit] in Counter.addOne
    [junit] in TestCounter.teardown
    [junit] ------------- ---------------- ---------------
    [junit] ------------- Standard Error -----------------
    [junit] java.lang.instrument.IllegalClassFormatException: Error while instrumenting class Counter2.
    [junit]     at org.jacoco.agent.rt_plkeqq.CoverageTransformer.transform(CoverageTransformer.java:94)
    [junit]     at sun.instrument.TransformerManager.transform(TransformerManager.java:188)
    [junit]     at sun.instrument.InstrumentationImpl.transform(InstrumentationImpl.java:424)
    [junit]     at sun.instrument.InstrumentationImpl.redefineClasses0(Native Method)
    [junit]     at sun.instrument.InstrumentationImpl.redefineClasses(InstrumentationImpl.java:170)
    [junit]     at mockit.internal.RedefinitionEngine.redefineMethods(RedefinitionEngine.java:244)
    [junit]     at mockit.internal.RedefinitionEngine.redefineMethods(RedefinitionEngine.java:238)
    [junit]     at mockit.internal.RedefinitionEngine.redefineMethods(RedefinitionEngine.java:166)
    [junit]     at mockit.internal.RedefinitionEngine.redefineMethods(RedefinitionEngine.java:159)
    [junit]     at mockit.Mockit.setUpMock(Mockit.java:425)
    [junit]     at TestCounter.setUp(TestCounter.java:13)
    [junit]     at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    [junit]     at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    [junit]     at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    [junit]     at java.lang.reflect.Method.invoke(Method.java:601)
    [junit]     at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:45)
    [junit]     at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
    [junit]     at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:42)
    [junit]     at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:27)
    [junit]     at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:30)
    [junit]     at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:263)
    [junit]     at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:68)
    [junit]     at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:47)
    [junit]     at org.junit.runners.ParentRunner$3.run(ParentRunner.java:231)
    [junit]     at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:60)
    [junit]     at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:229)
    [junit]     at org.junit.runners.ParentRunner.access$000(ParentRunner.java:50)
    [junit]     at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:222)
    [junit]     at org.junit.runners.ParentRunner.run(ParentRunner.java:300)
    [junit]     at junit.framework.JUnit4TestAdapter.run(JUnit4TestAdapter.java:39)
    [junit]     at org.apache.tools.ant.taskdefs.optional.junit.JUnitTestRunner.run(JUnitTestRunner.java:520)
    [junit]     at org.apache.tools.ant.taskdefs.optional.junit.JUnitTestRunner.launch(JUnitTestRunner.java:1060)
    [junit]     at org.apache.tools.ant.taskdefs.optional.junit.JUnitTestRunner.main(JUnitTestRunner.java:911)
    [junit] Caused by: java.lang.NoSuchFieldException: $jacocoData
    [junit]     at java.lang.Class.getDeclaredField(Class.java:1899)
    [junit]     at org.jacoco.agent.rt_plkeqq.core.runtime.AbstractRuntime.disconnect(AbstractRuntime.java:88)
    [junit]     at org.jacoco.agent.rt_plkeqq.CoverageTransformer.transform(CoverageTransformer.java:90)
    [junit]     ... 32 more
    [junit] java.lang.instrument.IllegalClassFormatException: Error while instrumenting class Counter2.
    [junit]     at org.jacoco.agent.rt_plkeqq.CoverageTransformer.transform(CoverageTransformer.java:94)
    [junit]     at sun.instrument.TransformerManager.transform(TransformerManager.java:188)
    [junit]     at sun.instrument.InstrumentationImpl.transform(InstrumentationImpl.java:424)
    [junit]     at sun.instrument.InstrumentationImpl.redefineClasses0(Native Method)
    [junit]     at sun.instrument.InstrumentationImpl.redefineClasses(InstrumentationImpl.java:170)
    [junit]     at mockit.internal.RedefinitionEngine.redefineMethods(RedefinitionEngine.java:244)
    [junit]     at mockit.internal.RedefinitionEngine.redefineMethods(RedefinitionEngine.java:238)
    [junit]     at mockit.internal.RedefinitionEngine.restoreOriginalDefinition(RedefinitionEngine.java:285)
    [junit]     at mockit.internal.state.MockFixture.restoreAndRemoveRedefinedClasses(MockFixture.java:128)
    [junit]     at mockit.Mockit.tearDownMocks(Mockit.java:454)
    [junit]     at TestCounter.tearDown(TestCounter.java:20)
    [junit]     at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    [junit]     at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    [junit]     at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    [junit]     at java.lang.reflect.Method.invoke(Method.java:601)
    [junit]     at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:45)
    [junit]     at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
    [junit]     at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:42)
    [junit]     at mockit.integration.junit4.internal.JUnit4TestRunnerDecorator.invokeExplosively(JUnit4TestRunnerDecorator.java:53)
    [junit]     at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java)
    [junit]     at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:36)
    [junit]     at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:263)
    [junit]     at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:68)
    [junit]     at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:47)
    [junit]     at org.junit.runners.ParentRunner$3.run(ParentRunner.java:231)
    [junit]     at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:60)
    [junit]     at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:229)
    [junit]     at org.junit.runners.ParentRunner.access$000(ParentRunner.java:50)
    [junit]     at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:222)
    [junit]     at org.junit.runners.ParentRunner.run(ParentRunner.java:300)
    [junit]     at junit.framework.JUnit4TestAdapter.run(JUnit4TestAdapter.java:39)
    [junit]     at org.apache.tools.ant.taskdefs.optional.junit.JUnitTestRunner.run(JUnitTestRunner.java:520)
    [junit]     at org.apache.tools.ant.taskdefs.optional.junit.JUnitTestRunner.launch(JUnitTestRunner.java:1060)
    [junit]     at org.apache.tools.ant.taskdefs.optional.junit.JUnitTestRunner.main(JUnitTestRunner.java:911)
    [junit] Caused by: java.lang.NoSuchFieldException: $jacocoData
    [junit]     at java.lang.Class.getDeclaredField(Class.java:1899)
    [junit]     at org.jacoco.agent.rt_plkeqq.core.runtime.AbstractRuntime.disconnect(AbstractRuntime.java:88)
    [junit]     at org.jacoco.agent.rt_plkeqq.CoverageTransformer.transform(CoverageTransformer.java:90)
    [junit]     ... 33 more
    [junit] ------------- ---------------- ---------------


Also the report task shows that Counter.addOne was not covered, although looking at the output it was indeed executed.

I've attached a zip of a situation to reproduce this.  Just extracting the zip and running ant should show the problem I'm seeing.
JMockit_JaCoCoIssue.zip

Jim Anderson

unread,
Oct 3, 2012, 8:43:00 PM10/3/12
to jac...@googlegroups.com
Any thoughts on this issue?  Is this just a bug that needs to be reported somewhere?

Thanks,
--Jim

Marc Hoffmann

unread,
Oct 4, 2012, 3:46:36 AM10/4/12
to jac...@googlegroups.com
Hi,

sorry for the delayed answer due to lack of free resources. Yes, please
open an issue at https://github.com/jacoco/jacoco/issues. As it is
probably the combination of both tools you might also check with JMockit
team.

Best regards,
-marc

Marc R. Hoffmann

unread,
Oct 5, 2012, 12:17:05 PM10/5/12
to jac...@googlegroups.com
Hi Jim,

thanks for the reproducer! It helped me to understand what's going on here. The first time Counter2 is seen by the JaCoCo agent it has already been transformed before - no idea how this is possible. Probably the class is directly loaded and transformed by the JMockit agent. As a result JaCoCo can't re-transform the class any more (JaCoCo adds fields and methods to classes which is not allowed for re-transformations).

As a work around you can simply the class before JMockit loads it. For this in your TestCounter add:

    @Before
    public void setUp() {
        new Counter2(); // Force loading of this class

        Mockit.setUpMock(MockCounter.class);
        Mockit.setUpMock(MockCounter2.class);
        System.out.println("in TestCounter.setup");
    }

As soon as time permits I'll investigate more on this issue.

Best regards,
-marc

Josef Frühwirth

unread,
Dec 12, 2012, 6:20:57 AM12/12/12
to jac...@googlegroups.com
Hi Marc,
I also ran into this issue after switching coverage analysis from cobertura to jacoco.

Classes and Tests are of course more complex as I tried to switch an existing project to jacoco.
I could not find a way to apply the workaround so far.

Is there investigation ongoing?
Is there already an issue available in the tracker dealing with this?

Best regards
Josef

Marc R. Hoffmann

unread,
Dec 12, 2012, 7:15:36 AM12/12/12
to jac...@googlegroups.com
Hi Josef,

there are several open issues with JaCoCo and mocking frameworks. There
are conceptional conflicts as well as implementation problems (probably
on both sides) which come from the fact that both tools do class file
transformations.

Best regards,
-marc
> --

avay...@googlemail.com

unread,
Dec 12, 2012, 8:40:06 AM12/12/12
to jac...@googlegroups.com
Hi,

the simple solution is using version 1.5.3. It works.


Josef Frühwirth

unread,
Dec 12, 2012, 9:19:28 AM12/12/12
to jac...@googlegroups.com
Hi Marc,
thx for your answer.

My impression is that jacoco shows most accurate results on coverage compared to other tools. I'd love to use it. But conflicts with mocking are really a show stopper. :-(

Is there coordination ongoing or planned between mocking frameworks (especially jmockit) and jacoco?

Regards
Josef

Marc R. Hoffmann

unread,
Dec 12, 2012, 4:10:15 PM12/12/12
to jac...@googlegroups.com
Hi Joseph,

no, resources are very limited here. We can't investigate this issue.
Note that Mockito perfectly works as long as you mock interfaces and
abstract classes only. Trouble starts as sson as you try to mock finals,
statics etc or spy obects.

Best regards,
-marc
> --

Marc R. Hoffmann

unread,
Dec 12, 2012, 4:11:40 PM12/12/12
to jac...@googlegroups.com
This is the EMMA based version of EclEmma. As everything is
pre-instrumented here on disk, the problem does not happen.

Best regards,
-marc

kolos...@gmail.com

unread,
Dec 14, 2012, 11:00:07 AM12/14/12
to jac...@googlegroups.com
Hi Marc,

What do you mean by this? Where does the problem not happen? When using Emma?

Kolos

amit...@gmail.com

unread,
Mar 20, 2014, 10:18:48 AM3/20/14
to jac...@googlegroups.com
I am getting the same error with jmockit and jacoco. Is there any update?

amit...@gmail.com

unread,
Mar 21, 2014, 3:05:01 AM3/21/14
to jac...@googlegroups.com
Solved it for my case.

I was getting this error when using @Cascading to mock. I changed that object to @Mock. The object created from this Mocked object was made @Cascading.

fantac...@gmail.com

unread,
Dec 16, 2015, 4:33:39 PM12/16/15
to JaCoCo and EclEmma Users
If the Jacoco only throws error for the classes that jmockit mocked, so I assume the coverage report will still be correct? Since we don't test the mocked classes and they should not be counted in the jacoco code coverage report. But when other tests execute the class that was previously mocked , will jacoco be able to calculate the code coverage correctly?

My question is that will this error, Class org/testng/internal/Parameters is already instrumented, affect the correctness of the code coverage report?

Marc R. Hoffmann

unread,
Dec 17, 2015, 1:41:32 AM12/17/15
to jac...@googlegroups.com
Hi,

JaCoCo can distinguish between different versions of a class. So in your
case the coverage for the non-mocked version of class "Parameters"
should be correct.

Regards,
-marc
Reply all
Reply to author
Forward
This conversation is locked
You cannot reply and perform actions on locked conversations.
0 new messages