I’m writing unit tests for my Java app and I need to write a test for a potential JiBX exception that can be thrown. The method I’m testing calls on a method from another class, where the JiBX exception can be potentially thrown. This is the class I’m testing (let’s call it Class A):
@Inject
private CommonDAL commonDAL;
@Async
public Future<String> getTransactionalXXXAvailability(
List<XXXAvailRequestEntry> requestEntries, TravelWindow travelWindow) {
if (requestEntries.size() == 0)
return null;
XXXAvailRqAccessor requestAccessor = new XXXAvailRequestBuilder().buildRequest(requestEntries, travelWindow);
logger.info(requestAccessor.marshalRequest());
String responseAsXml = null;
try {
responseAsXml = getResponse(requestAccessor.getRequest());
} catch (JiBXException e) {
logger.error("Problem unmarshaling the XXX avail response: ", e);
}
logger.info(responseAsXml);
return new AsyncResult<String>(responseAsXml);
}
private String getResponse(OTAXXXAvailRQ request) throws JiBXException {
HbsiConnectionInfo connectionInfo = new HbsiConnectionInfo();
connectionInfo.useConnectionInfoFromContext();
HBSIXML4OTAWebserviceSoap hbsiSoap = getHbsiSoapService(connectionInfo);
InterfacePayload header = new InterfacePayload();
header.setChannelIdentifierId("XXXXXXXXX");
header.setVersion("2005B");
header.setInterface("HBSI XML 4 OTA");
ComponentInfo componentInfo = new ComponentInfo();
XXXAvailRqAccessor requestAccessor = new XXXAvailRqAccessor(request);
componentInfo.setId(requestAccessor.getFirstXXXCode());
componentInfo.setUser( connectionInfo.getUsername() );
componentInfo.setPwd( connectionInfo.getPassword() );
componentInfo.setComponentType(EComponentType.XXX);
Login login = new Login();
login.setComponentInfo(componentInfo);
Message body = new Message();
// todo: this needs to be unique for every request.
// todo: hook up to logging
body.setRequestId(UUID.randomUUID().toString());
body.setTransaction(ETransaction.XXX_AVAIL_RQ);
body.setXML(requestAccessor.marshalRequest());
return hbsiSoap.getSoapRequest(header, body, login);
}
HBSIXML4OTAWebserviceSoap getHbsiSoapService(HbsiConnectionInfo connectionInfo) {
HBSIXML4OTAWebservice ws = new HBSIXML4OTAWebservice( connectionInfo.getWsdlLocation() );
HBSIXML4OTAWebserviceSoap hbsiSoap = ws.getHBSIXML4OTAWebserviceSoap();
Map<String, Object> requestContext = ((BindingProvider)hbsiSoap).getRequestContext();
String readTimeout = commonDAL.getPropertyValue(new PropertyKey(Section.HBSI,
Property.HBSI_WS_READ_TIMEOUT));
requestContext.put(BindingProviderProperties.REQUEST_TIMEOUT, Integer.parseInt(readTimeout));
String connectionTimeout = commonDAL.getPropertyValue(new PropertyKey(Section.HBSI,
Property.HBSI_WS_CONNECTION_TIMEOUT));
requestContext.put(BindingProviderProperties.CONNECT_TIMEOUT, Integer.parseInt(connectionTimeout));
return hbsiSoap;
}
The method that throws the error is as follows (and from another class, let’s call it Class B):
public String marshalRequest() {
StringWriter requestAsXml = new StringWriter();
try {
IBindingFactory bindingFactory = BindingDirectory.getFactory(PROTECTEDCLASSNAME.class);
IMarshallingContext marshalingContext = bindingFactory.createMarshallingContext();
marshalingContext.setIndent(2);
marshalingContext.setOutput(requestAsXml);
marshalingContext.marshalDocument(request);
} catch (JiBXException e) {
logger.error("Problem marshaling PROTECTEDCLASSNAME.", e);
}
return requestAsXml.toString();
}
When “body.setXML(requestAccessor.marshalRequest());” is called, another class (requestAccessor) is visited by the test, and it’s method .marshalRequest is where the JiBX exception is supposed to be thrown. The purpose of the tests I’m writing is to get this Class A’s unit test coverage to 100&, but the system under test is composed of at least two classes as I can’t mock XXXAvailRqAccessor object called requestAccessor. I can’t get any tests to produce this error, for the following reasons.
-
The XXXAvailRqAccessor object called requestAccessor is instantiated inside the methods I’m testing so I can’t use a mock to throw an exception.
-
the OTAXXXAvailRQ argument passed to .getResponse() cannot be mocked because it’s created by the builder for XXXAvailRqAccessor.
-
I tried spying on IBindingFactory, but it didn’t work. I created a method in Class B that would instantiate an IBindingFactory so I could spy on it, but that didn’t work.
-
I also tried using PowerMock to return a mock XXXAvailRqAccessor when it is instantiated, however when I attempted to mock a JiBXExceptioin for .getRequest, Mockito said “Checked exception is invalid for this method”. If I can’t get Mockito to throw this error, I don’t know if it’s possible to manipulate the associated objects to throw it.
Well not really, or I don’t know of such way at least. You could, if you REALLY want to do it (I’m against it) create a method like this in that class:
And replace this line:
With:
Then you can spy() (you can read on Mockito.spy() in the documentation if you’re not familiar with it) this object and make this method return a mock. From that point it’s smooth sailing.
This approach is not adviced though because:
The question remains: how to properly test such cases. Well in most situations I try to refactor as much as possible and sometimes it helps. And in other cases… Well I still did not come up with a better solution.