Igor Kromin |   Consultant. Coder. Blogger. Tinkerer. Gamer.

While adding JUnit unit tests to one of the projects I've contributed to I've come across a SOAPHandler implementation that was completely untested. I couldn't let that be so decided that at least some test coverage was required. Unfortunately testing a SOAPHandler is not so easy, but with a bit of Mockito magic is very doable.
mockito.jpg


The problem with SOAPHandler testing is the handleMessage() method uses a SOAPMessageContext object that has quite deeply nested objects for anything useful. What I wanted to test specifically was whether the value from the wsa:MessageID header element was being populated correctly in my Log4J MDC. This meant I needed to access the SOAP headers. To get to the headers you need to traverse an ugly chain of objects: context.getMessage().getSOAPPart().getEnvelope().getHeader().examineAllHeaderElements().

Boo!

Creating dummy implementations of all those classes by hand would have been quite painful as they have dozens of methods that needed to be written. With Mockito however, it was just a few lines of code.

So my approach was to create a bunch of mock objects that returned the values I was looking for and it worked like a charm. With just the basic test method most of the SOAPHandler I was testing was covered.

Since I was looking for the wsa:MessageID element, I needed to create a Node mock object first like this:
 Java
String myMessageId = "MyMessageID1";
Node messageIdNode = mock(Node.class);
when(messageIdNode.getLocalName()).thenReturn("MessageID");
when(messageIdNode.getValue()).thenReturn(myMessageId);




Then I added the Node to an ArrayList so that I could return an Iterator later.
 Java
ArrayList<Node> headers = new ArrayList<>();
headers.add(messageIdNode);


Next was the mock object for the context itself. Note that RETURNS_DEEP_STUBS is used when creating the context mock object, this is so that the ugly sequence of calls I talked about above will work (inside my SOAPHandler code). The SOAPHandler was using examineAllHeaderElements() to inspect the passed in SOAP headers so I mocked that up to return an iterator to the array of headers created above.
 Java
SOAPMessageContext context = mock(SOAPMessageContext.class, RETURNS_DEEP_STUBS);
when(context.get(SOAPMessageContext.MESSAGE_OUTBOUND_PROPERTY))
.thenReturn(Boolean.FALSE);
when(context.getMessage().getSOAPPart().getEnvelope().getHeader().examineAllHeaderElements())
.thenReturn(headers.iterator());


That's it for the mock objects so the final touch was to actually create an instance of the SOAPHandler, pass it the mocked up context and test whether the Log4J MDC was populated correctly:
 Java
MySoapHandler handler = new MySoapHandler();
handler.handleMessage(context);
Assert.assertEquals(myMessageId, MDC.get(MySoapHandler.MESSAGE_ID));


Nice and clean!

Of course in order for this to work you need to have some annotations on your class and test method. Below is a skeleton class and test method that could be used as an example:
 Java
package net.igorkromin;
import javax.xml.soap.Node;
import javax.xml.ws.handler.soap.SOAPMessageContext;
import java.util.ArrayList;
import org.apache.log4j.MDC;
import org.junit.*;
import org.junit.runner.RunWith;
import org.mockito.runners.MockitoJUnitRunner;
import static org.mockito.Mockito.*;
@RunWith(MockitoJUnitRunner.class)
public class MySoapHandlerTest
{
@Test public void testHandler() throws Exception
{
...
}
}


-i

A quick disclaimer...

Although I put in a great effort into researching all the topics I cover, mistakes can happen. Use of any information from my blog posts should be at own risk and I do not hold any liability towards any information misuse or damages caused by following any of my posts.

All content and opinions expressed on this Blog are my own and do not represent the opinions of my employer (Oracle). Use of any information contained in this blog post/article is subject to this disclaimer.
Hi! You can search my blog here ⤵
NOTE: (2022) This Blog is no longer maintained and I will not be answering any emails or comments.

I am now focusing on Atari Gamer.