JMockit gives mockit.internal.UnexpectedInvocation when I try to instrument the code with clover.

vinny63 April 27, 2015

I have tests that use jmockit and when I try to instrument it using Clover I get the following exception:

mockit.internal.UnexpectedInvocation: Unexpected invocation of:
java.io.File#File(String)

 with arguments: 
"C:\git\ng-cloud\tools\appfabric\maven\package-code-scan-plugin\.clover\coverage.db1f6pnu9s8ffmg_1_an8s56_i907uj4k.s"
 on mock instance: java.io.File@683a412e
 at java.io.File.<init>(File.java)
 at com.atlassian.clover.recorder.FileBasedPerTestRecording.<init>(FileBasedPerTestRecording.java:33)
 at com.atlassian.clover.recorder.FileBasedPerTestRecording.<init>(FileBasedPerTestRecording.java:51)
 at com.atlassian.clover.recorder.ActivePerTestRecorderOne.testFinished(ActivePerTestRecorderOne.java:67)
 at com.atlassian.clover.recorder.ThreadVisibilityStrategy$SingleThreaded.testFinished(ThreadVisibilityStrategy.java:119)
 at com.atlassian.clover.recorder.BaseCoverageRecorder.sliceEnd(BaseCoverageRecorder.java:376)
 at com_atlassian_clover.Clover$InitialisedRuntime$4.run(Clover.java:675)
 at com_atlassian_clover.Clover$InitialisedRuntime$4.run(Clover.java:668)
 at java.security.AccessController.doPrivileged(Native Method)
 at com_atlassian_clover.Clover$InitialisedRuntime.allRecordersSliceEnd(Clover.java:668)
 at com_atlassian_clover.Clover.allRecordersSliceEnd(Clover.java:144)
 at com.atlassian.clover.recorder.BaseCoverageRecorder.globalSliceEnd(BaseCoverageRecorder.java:137)
 at com.org.application.maven.DirectoryScannerTest.verifyDirectoryExistsNullFiles(DirectoryScannerTest.java:131)

 

My test code is as follows:

@Test
 public void verifyDirectoryExistsSuccess(@Mocked final List<File> filesToVerify,
 @Mocked final File file,
 @Mocked final Iterator it) throws IllegalArgumentException {final File[] files = new File[1];
new Expectations() {{
 filesToVerify.isEmpty(); result = Boolean.FALSE; times = 1;
 filesToVerify.iterator(); result = it; times = 1;
 it.hasNext(); result = Boolean.TRUE; times = 1;
 it.next(); result = file; times = 1;
 file.exists(); result = Boolean.TRUE; times = 1;
 it.hasNext(); result = Boolean.FALSE; times = 1;
 }};
 final DirectoryScanner ds = new DirectoryScanner(null, "filetest", null);
 assertEquals(1, ds.verifyDirectoriesExist(filesToVerify).size());
 }

1 answer

1 accepted

1 vote
Answer accepted
Marek Parfianowicz
Atlassian Team
Atlassian Team members are employees working across the company in a wide variety of roles.
April 27, 2015

Hi Vinayak,

I think I've figured it out. There are several issues in your code sample.

 

1) Special handling of Expectations / Verifications blocks by JMockit.

The following example when instrumented by Clover:

@Test
public void doFileNoCloverOff() throws Exception {
    new Expectations() {{

    }};
}

fails with an error message:

doFileNoCloverOff(MyFileTest)  Time elapsed: 0.327 sec  <<< ERROR!
java.lang.IllegalArgumentException: Invalid try/finally statement inside expectation block
        at MyFileTest$1.<init>(MyFileTest.java)
        at MyFileTest.__CLR4_0_4k5yzrl1z(MyFileTest.java:16)
        at MyFileTest.doFileNoCloverOff(MyFileTest.java:15)

 

Clover adds it's own instrumentation code inside the Expectations block (as it sees it as a normal instance initializer). At the same time JMockit treats code inside the Expectations block in a special way - not as a regular code, but as a list of expected invocations.

You have to disable Clover instrumentation for the Expectations block - surround it with ///CLOVER:OFF and ///CLOVER:ON comments, as it was described here:

https://answers.atlassian.com/questions/12273406

 

2) Using full mocks for classes from JDK

The following example when instrumented by Clover:

@Test
public void doFileFullMock(@Mocked final File file) throws Exception {
    final File someFile = new File("C:\\Temp");

    ///CLOVER:OFF
    new Expectations() {{
        someFile.getAbsolutePath(); result = "XYZ";
    }};
    ///CLOVER:ON

    assertEquals("XYZ", someFile.getAbsolutePath()); // calls mocked method
}

compiles and test succeeds, but Clover fails with an error like this:

ERROR: CLOVER: Unable to load the coverage database at "null"
ERROR: CLOVER: No coverage data will be gathered.

 

It fails because your test is mocking java.io.File class, which is being used by Clover to write code coverage file to disk. Due to a fact that full mock is used, all methods of the mocked class return null value, unless they are specified in the Expectations block.

 

In order to fix this you have to use a partial mock, so that original methods will be called (unless specified in the Expectations block).

 

@Test
public void doFilePartialMock() throws Exception {
    final File someFile = new File("C:\\Temp");

    ///CLOVER:OFF
    new Expectations(File.class) {{ // a partial mock
        someFile.getAbsolutePath(); result = "XYZ";
    }};
    ///CLOVER:ON

    assertTrue(someFile.exists()); // calls original method
    assertEquals("XYZ", someFile.getAbsolutePath()); // calls mocked method

    // when instrumented, Clover calls its own code here, 
    // which writes per-test coverage file to disk
}

 

See http://jmockit.org/tutorial/BehaviorBasedTesting.html#partial

 

Cheers
Marek

vinny63 April 27, 2015

I made the changes as you had suggested but I am having no luck when I do clover2:instrument.By the way I am using java 7.

Marek Parfianowicz
Atlassian Team
Atlassian Team members are employees working across the company in a wide variety of roles.
April 27, 2015

Can you confirm that you've added "new File(anyString); minTimes = 0;" too?

Marek Parfianowicz
Atlassian Team
Atlassian Team members are employees working across the company in a wide variety of roles.
April 28, 2015

What error message do you see? What flush policy settings do you use? What JMockit version do you use? I'm asking because I've noticed a slightly different behaviour than described by you.

vinny63 April 28, 2015

I have added new File(anyString); minTimes = 0 and the tags to switch off and on clover for expectation block.But still I get the invocation error.Here is the stacktrace: erifyDirectoryExistsSuccess(com.org.app.maven.DirectoryScannerTest) Time elapsed: 0.019 sec <<< ERROR! mockit.internal.UnexpectedInvocation: Unexpected invocation of: java.io.File#File(String) with arguments: "C:\git\next\tools\app\maven\package-code-scan-plugin\target\clover\clover.dbru9c6j24nagh_1j_vkgrxe_i91d1n4n.s" on mock instance: java.io.File@101b57f7 at java.io.File.<init>(File.java) at com.atlassian.clover.recorder.FileBasedPerTestRecording.<init>(FileBasedPerTestRecording.java:33) at com.atlassian.clover.recorder.FileBasedPerTestRecording.<init>(FileBasedPerTestRecording.java:51) at com.atlassian.clover.recorder.ActivePerTestRecorderMany.testFinished(ActivePerTestRecorderMany.java:45) at com.atlassian.clover.recorder.ThreadVisibilityStrategy$SingleThreaded.testFinished(ThreadVisibilityStrategy.java:119) at com.atlassian.clover.recorder.BaseCoverageRecorder.sliceEnd(BaseCoverageRecorder.java:376) at com_atlassian_clover.Clover$InitialisedRuntime$4.run(Clover.java:675) at com_atlassian_clover.Clover$InitialisedRuntime$4.run(Clover.java:668) at java.security.AccessController.doPrivileged(Native Method) at com_atlassian_clover.Clover$InitialisedRuntime.allRecordersSliceEnd(Clover.java:668) at com_atlassian_clover.Clover.allRecordersSliceEnd(Clover.java:144) at com.atlassian.clover.recorder.BaseCoverageRecorder.globalSliceEnd(BaseCoverageRecorder.java:137) at com.org.app.maven.DirectoryScannerTest.verifyDirectoryExistsSuccess(DirectoryScannerTest

Marek Parfianowicz
Atlassian Team
Atlassian Team members are employees working across the company in a wide variety of roles.
April 29, 2015

Please try to recompile your code. I've been running some tests and found a workaround for a mocked File class. See my updated answer.

Suggest an answer

Log in or Sign up to answer
TAGS
AUG Leaders

Atlassian Community Events