1

I am running into a problem mocking out java.lang.Math using JMockit (1.21). See below for a simplified use of my actual class. Basically somewhere in my code I use Math.pow(...) and I whish to mock it.

public final class Calculator {
    public final double power(double base, double exponent) {
        return Math.pow(base, exponent);
    }
}

As for my test code, this test works.

@Test
public void simpleTestWithoutMock() {
    Calculator calculator = new Calculator();
    double result = calculator.power(2, 3);
    assertThat(result).isEqualTo(8);
}

This test fails.

@Test
public void simpleTestWithMock(@Mocked Math math) {
    new Expectations() {{ // This would be line 67 in below stracktrace
        Math.pow(2,3); result = 1;
    }};

    Calculator calculator = new Calculator();
    double result = calculator.power(2, 3);
    assertThat(result).isEqualTo(1);
}

The error message I get:

java.lang.NoClassDefFoundError: nl/endran/sandbox/CalculatorTest$1

        at nl.endran.sandbox.CalculatorTest.simpleTestWithMock(CalculatorTest.java:67)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at java.lang.reflect.Method.invoke(Method.java:483)
        at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69)
        at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:234)
        at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:74)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at java.lang.reflect.Method.invoke(Method.java:483)
        at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)
        Caused by: java.lang.ClassNotFoundException: nl.endran.sandbox.CalculatorTest$1
        at java.net.URLClassLoader$1.run(URLClassLoader.java:372)
        at java.net.URLClassLoader$1.run(URLClassLoader.java:361)
        at java.security.AccessController.doPrivileged(Native Method)
        at java.net.URLClassLoader.findClass(URLClassLoader.java:360)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
        ... 9 more

I am able to mock other system classes like System and RunTime like this, but Math just doesn't seem to work (nor does String for that matter). I know how to circumvent this is my tests, so I am not blocked or anything, but I cannot understand why Math cannot be mocked. Removing the Expectations block will not cause the NoClassDefFoundError error anymore. I suspect methods that have a signature like public static native to play a role in this, but I cannot find anything.

DwB
  • 37,124
  • 11
  • 56
  • 82
Endran
  • 1,627
  • 1
  • 10
  • 15
  • 2
    consider this: don't mock Math. – DwB Feb 22 '16 at 19:12
  • http://stackoverflow.com/questions/26408253/how-to-mock-a-static-method-from-jmockit – Ammar Feb 22 '16 at 19:15
  • 1
    @DwB, Yeah, i know that will work, but that doesn't answer my question. What's so special about `Math` that makes it unmockable? – Endran Feb 22 '16 at 19:15
  • @Ammar, I am not asking how to mock a static method, I am asking why can't I mock Math? – Endran Feb 22 '16 at 19:16
  • Consider asking the question you want answered. Instead of "How many blueberries fit in a petunia?" ask "How can I fit blueberries in a petunia?" if what you want to know is "How can I fit blueberries in a petunia?" – DwB Feb 22 '16 at 19:19

1 Answers1

1

Because 1) methods from java.lang.Math (in particular, Math.min(a, b)) get called all the time by other classes from the JRE (for example, look inside java.lang.String), and 2) when JMockit mocks a class (with @Mocked, anyway), it gets mocked for the duration of the test, regardless of where the calls are coming from.

Bottom line, don't mock low-level JRE classes unless you really know what you are doing. In this particular case, JMockit should probably deny the full mocking of java.lang.Math, as really there is no good reason (that I can see) for doing it.

Rogério
  • 16,171
  • 2
  • 50
  • 63
  • I can agree for not having a proper reason to mock Math, apart from Math.random().. Also I can see the dependencies of the entire system on these low level classes. However, I think that both point 1 and 2 might be a sufficient reason as to why not to mock lower level classes, still I would like to see some formal proof. – Endran Feb 25 '16 at 23:03
  • How about in order to use Verifications to make sure `pow()` was called? I agree mocking `Math` is a bad idea, but verifying seems to be a good idea... In order to avoid discussion in the comments, I've created [this question](http://stackoverflow.com/questions/35727719/how-to-use-verifications-on-an-un-mockable-type-in-jmockit) to address this issue. – dcsohl Mar 01 '16 at 16:07