vendredi 1 mai 2015

How to partial mock a method that throws exceptions using Mockito?

Use Case

It's useful to test exception handling. In this specific case, I have a extractor that will do a specific task when an exception is thrown while unmarshaling a specific class.

The signature of the method that I want to mock is:

public <T> T unmarshaler(Node element, Class<? extends T> type) throws TransformException

Things I've Tried

Here's a list of things I've tried. For brevity, I haven't filled in all the mundane details.

Spying

The following method doesn't do anything and the exception doesn't throw. I'm not sure why.

XmlMarshaler spiedXmlMarshaler = Mockito.spy(fixture.xmlMarshaler);
doThrow(new TransformException( ... )).when(spiedXmlMarshaler)
    .unmarshal(any(), eq(SpecificThing.class));

Mocking

The following didn't work because partial mocks don't seem to play well with methods that throw exceptions. Instead, it passes the arguments null, null to the real method each time.

XmlMarshaler mockXmlMarshaler = mock(XmlMarshaler.class);
Mockito.when(mockXmlMarshaler.unmarshal(any(), any())).thenCallRealMethod();
Mockito.when(mockXmlMarshaler.unmarshal(any(), eq(SpecificThing.class))
       .thenThrow(new TransformException(...));

ThenAnswer

This works, but I'm not sure if it's the proper way to do this:

final XmlMarshaler realXmlMarshaler = new XmlMarshaler();
XmlMarshaler mockXmlMarshaler = Mockito.mock(XmlMarshaler.class);
fixture.xmlMarshaler = mockXmlMarshaler;
Mockito.when(mockXmlMarshaler.unmarshal(any(), any())).thenAnswer(new Answer() {

    @SuppressWarnings({ "unchecked" })
    @Override
    public Object answer(InvocationOnMock invocation) throws Throwable {
        Object[] args = invocation.getArguments();

        Node node = (Node)args[0];
        Class clazz = (Class)args[1];

        if (clazz.equals(SpecificThing.class)) {
            throw new TransformException(...);
        }

        return realXmlMarshaler.unmarshal(node, clazz);
    }

});

Question

Although the thenAnswer method works, it doesn't seem to be the proper solution. What is the correct way to perform a partial mock for this situation?

Aucun commentaire:

Enregistrer un commentaire