Search code examples
javajmockit

JMockit not matching recorded expectation


I am attempting to mock the SAXParser.parse(...) methods using JMockit.

I am able to successfully setup Expectations for the parse(InputStream, DefaultHandler) method signature, but when I attempt to mock the parse(InputSource, DefaultHandler) signature, JMockit never sees the invocation and throws a MissingInvocation exception.

The following example shows two test cases, one mocking the InputSource flavor and one mocking the InputStream flavor:

public class SAXTest {

    @Test(expected=RuntimeException.class)
    public void testParseInputSource(@Mocked final SAXParser saxParser) throws Exception {
        new Expectations() {{
            saxParser.parse((InputSource) any, (DefaultHandler) any); result = new RuntimeException("Fail now");
        }};

        SAXParser p = SAXParserFactory.newInstance().newSAXParser();
        InputSource isource = new InputSource(new StringReader("<test/>"));
        p.parse(isource, new DefaultHandler());    
    }

    @Test(expected=RuntimeException.class)
    public void testParseInputStream(@Mocked final SAXParser saxParser) throws Exception {
        new Expectations() {{
            saxParser.parse((InputStream) any, (DefaultHandler) any); result = new RuntimeException("Fail now");
        }};

        SAXParser p = SAXParserFactory.newInstance().newSAXParser();
        InputStream istream = new ByteArrayInputStream("</test>".getBytes());
        p.parse(istream, new DefaultHandler());
    }
}

Running the testcase results in:

    JUnit version 4.12
E.
Time: 0.069
There was 1 failure:
1) testParseInputSource(SAXTest)
java.lang.Exception: Unexpected exception, expected<java.lang.RuntimeException> but was<mockit.internal.MissingInvocation>
        at org.junit.internal.runners.statements.ExpectException.evaluate(ExpectException.java:28)
 ...
Caused by: Missing 1 invocation to:
javax.xml.parsers.SAXParser#parse(any org.xml.sax.InputSource, any org.xml.sax.helpers.DefaultHandler)
   on mock instance: $Subclass_SAXParser_param0@490d6c15
Caused by: Missing invocations
        at javax.xml.parsers.SAXParser.parse(SAXParser.java)
        at SAXTest$1.<init>(SAXTest.java:20)
        at SAXTest.testParseInputSource(SAXTest.java:19)

FAILURES!!!
Tests run: 2,  Failures: 1

As you can see, the InputSource version fails, while the InputStream version works as expected.

I am using Junit 4.12 and JMockit 1.32


Solution

  • TL;DR

    Use the @Capturing annnotation instead of the @Mocked one on the InputSource test.

    Explanation

    Your problem is that SAXParser.parse(InputSource, DefaultHandler) method is overridden by SAXParserImpl. As you're using the @Mocked annotation, you are mocking only the methods from SAXParser class.

    SAXParserFactory.newInstance().newSAXParser() returns a SAXParserImpl, so for the InputStream method, as there is no override, it goes to the mocked SAXParser.parse(InputStream, DefaultHandler) method.

    BUT, for the InputSource method, as there is an override, it goes to the not mocked method, and hence the missing invocation.

    If you were to use the Capturing annotation, that mocks an interface/class and all of its sub-classes/sub-implementations, you won't have the missing invocation exception.