0

I'm trying to mock a method that creates a local variable, tries something, and does logging if an exception is thrown. Here's a snippet of code:

public void myFunction() {
    //Some stuff
    try {
        new File("foo").getAbsoluteFile();
    } catch (SecurityException e) {
        //Do some logging
    }
}

I'd like to mock this logging behavior using JMockit (using version 1.8 if it matters). So I created the following test:

@Test(expected=SecurityException.class)
public void testAbsoluteFile(
        @Injectable final File file
) throws IOException {

    new Expectations(File.class){{
        new File(anyString);
        result = file;
        file.getAbsoluteFile();
        result = new SecurityException();
    }};

    myFunction();
}

The trouble is that this seems to give me a NullPointerException on the inner workings of File.getAbsoluteFile(), which I find absolutely bizarre:

java.lang.Exception: Unexpected exception, expected<java.lang.SecurityException> but was<java.lang.NullPointerException>
Caused by: java.lang.NullPointerException
    at java.io.Win32FileSystem.slashify(Win32FileSystem.java:56)
    at java.io.Win32FileSystem.resolve(Win32FileSystem.java:330)
    at java.io.File.getAbsolutePath(File.java:556)
    at java.io.File.getAbsoluteFile(File.java:572)
    at com.vue.rescoreservice.ExpectationTest.myFunction(ExpectationTest.java:39)
    at com.vue.rescoreservice.ExpectationTest.testAbsoluteFile(ExpectationTest.java:33)

This seems really bizarre as it's saying that a local variable in the Win32FileSystem class (an internal class not in the typical Java API) is throwing a NullPointerException, when it never did before.

The lines that are in the stack trace are as follows:

//In myFunction()
new File("foo").getAbsoluteFile();

and

//In testAbsoluteFile()
myFunction();

Why is this happening? And how can I make it so that JMockit does not throw a NullPointerException on local variables of internal classes?

Thunderforge
  • 19,637
  • 18
  • 83
  • 130

2 Answers2

3

That issue has been fixed in the latest jMockit version (1.14). If you don't want to migrate now, it's possible to fix the test in 1.8 (see the code below).

In this case, the @Injectable isn't necessary. The constructor of File is mocked for the Expectations and that requires the mocking of the class itself instead of a single instance. In that case, the behavior is equivalent to @Mocked (but File will be partially mocked according to the calls in the Expectations block).

The catch clause in myFunction needs to rethrow the SecurityException to allow the test to pass.

@Test(expected=SecurityException.class)
public void testAbsoluteFile() throws IOException {
    new Expectations(File.class) {{
        File file = new File(anyString);

        file.getAbsoluteFile();
        result = new SecurityException();
    }};

    myFunction();
}

If you prefer to declare the mock as a parameter, it will also work, but File won't be partially mocked (all the methods will be mocked).

@Test(expected=SecurityException.class)
public void testAbsoluteFile(@Mocked final File file) throws IOException {

    new Expectations(){{
        new File(anyString);

        file.getAbsoluteFile();
        result = new SecurityException();
    }};

    myFunction();
}
Marc-André
  • 846
  • 6
  • 8
  • 1
    The reason why the test fails in java.io.Win32FileSystem is that the File class isn't mocked correctly and the real implementation is called with anyString as a parameter. The value of anyString is null, so it fails with a NullPointerException. – Marc-André Jan 17 '15 at 03:03
  • 1
    Great answer! Just a nitpick: it's best to partially mock `File` for a specific file name here => `new Expectations(File.class) {{ new File("foo").getAbsoluteFile(); result = new SecurityException(); }};` – Rogério Jan 18 '15 at 16:26
1

An update on @Marc-André answer.

In my case with JMockit 1.25 I had to define the file variable outside of the expectations leaving it like:

@Test(expected=SecurityException.class)
public void testAbsoluteFile() throws IOException {
    File file = new File("");
    new Expectations(File.class) {{
        file.getAbsoluteFile();
        result = new SecurityException();
    }};

    myFunction();
}

Maybe it was a change on JMockit prior to the answer.

Alfergon
  • 5,463
  • 6
  • 36
  • 56