Search code examples
sbtjmockit

Can SBT work with jMockit?


After many struggles I finally got a large project converted over from Maven to SBT. One of the remaining issues, however, is that some of the unit tests in the project use jMockit which can be a bit high-maintenance when it comes to configuring the environment.

Specifically the jmockit dependency/jar has two difficult requirements:

  1. The jmockit jar must appear in the classpath before the junit jar
  2. On many JVM's, such as the OpenJDK one I'm using, the JVM argument -javaagent:<path/to/jmockit.jar> is required

If both of these conditions are not met, I'm faced with the error:

[error] Test <mytestclass>.initializationError failed: java.lang.Exception: Method <mytestmethod> should have no parameters
[error]     at mockit.integration.junit4.JMockit.<init>(JMockit.java:32)

I think I eventually managed to take care of #1 with SBT but I'm still having trouble with the second one. The debug SBT logs do not show enough detail about the forked process invocation to tell me if my settings are working or not. But the test output consistently indicates that it's not working. I have what I think are all the relevant settings:

lazy val myproj = Project(
    ...
    settings = otherSettings ++ Seq(
        libraryDependencies ++= Seq(
                 "com.googlecode.jmockit" % "jmockit" % "1.7" % "test",
                 "junit" % "junit" % "4.8.1" % "test"
        ),
        fork in Test := true,
        javaOptions in test += "-javaagent:<hardcode-path-to-jmockit.jar>"
    )

I think the classpath is OK based on the output of the test:dependencyClasspath:

sbt> project <myproject>
sbt> show test:dependencyClasspath
[info] List(...., Attributed(/var/build/ivy2/cache/junit/junit/jars/junit-4.8.1.jar), ...
..., Attributed(/var/build/ivy2/cache/com.googlecode.jmockit/jmockit/jars/jmockit-1.7.jar), ...)

So I'm thinking that my javaagent setting is not having the intended result.

If I do happen to get this to work, my next question is how to get the hard-coded jmockit.jar path out of there but for now I'll settle for a passing test case.

So, how do I set the JVM options used for testing? How do I view/verify what options are actually used when the tests are launched?


Solution

  • You need to change your javaOptions to javaOptions in Test (note the T in Test is capitalized).

    You can check your options by executing show test:javaOptions

    > show test:javaOptions
    [info] List(-javaagent:/home/lpiepiora/.ivy2/cache/com.googlecode.jmockit/jmockit/jars/jmockit-1.7.jar)
    

    Additionally if you want to use dynamic path to the jmockit jar, you can set your javaOptions like this:

    def jmockitPath(f: Seq[File]) = f.filter(_.name.endsWith("jmockit-1.7.jar")).head
    
    javaOptions in Test += s"-javaagent:${jmockitPath((dependencyClasspath in Test).value.files)}"
    

    build.sbt for reference

    libraryDependencies += "com.novocode" % "junit-interface" % "0.9" % "test"
    
    libraryDependencies ++= Seq(
      "com.googlecode.jmockit" % "jmockit" % "1.7" % "test",
      "junit" % "junit" % "4.8.1" % "test"
    )
    
    fork in Test := true
    
    def jmockitPath(f: Seq[File]) = f.filter(_.name.endsWith("jmockit-1.7.jar")).head
    
    javaOptions in Test += s"-javaagent:${jmockitPath((dependencyClasspath in Test).value.files)}"