For below method, is there a way to create unit test to cause DatatypeConfigurationException
, so I can test that it threw ConversionException
?
Here is my code:
public static XMLGregorianCalendar getXMLGregorianCalendar(final LocalDate localDate) {
XMLGregorianCalendar xmlGregorianCalendar = null;
if (localDate != null) {
final String dateString = localDate.format(yyyMMddFormat);
try {
xmlGregorianCalendar = DatatypeFactory.newInstance().newXMLGregorianCalendar(dateString);
} catch (DatatypeConfigurationException e) {
throw new ConversionException("Unable to format LocalDate.", e);
}
}
return xmlGregorianCalendar;
}
You can override the implementation that the factory will create by setting a system property with a classname to instantiate. Then that class can throw an exception in that method.
For example like this
public class FailingDatatypeFactory implements DatatypeFactory {
public XMLGregorianCalendar newXMLGregorianCalendar() { throw new DatatypeConfigurationException() }
}
and then set it up like so
System.setProperty("javax.xml.datatype.DatatypeFactory", FailingDatatypeFactory.class.getName());
Now after you run your test case you should clear the property so no other tests try to instantiate that implementation.
Here the checked exception is thrown by javax.xml.datatype.DatatypeFactory.newInstance()
.
It is a static method. So you cannot mock it straightly.
1) As alternative you could try to find the scenario that could provoke the exception to be risen.
Let's go. The exception is throw here :
private static <T> T findServiceProvider(final Class<T> type)
throws DatatypeConfigurationException{
try {
return AccessController.doPrivileged(new PrivilegedAction<T>() {
public T run() {
final ServiceLoader<T> serviceLoader = ServiceLoader.load(type);
final Iterator<T> iterator = serviceLoader.iterator();
if (iterator.hasNext()) {
return iterator.next();
} else {
return null;
}
}
});
} catch(ServiceConfigurationError e) {
final DatatypeConfigurationException error =
new DatatypeConfigurationException(
"Provider for " + type + " cannot be found", e);
throw error;
}
}
So DatatypeConfigurationException
is thrown when ServiceConfigurationError
is thrown and caught. But ServiceConfigurationError
is an error and not an exception.
Trying to simulate an error becomes very hacky.
2) Other alternative to test it : wrapping DatatypeFactory.newInstance()
in an instance of your own class.
In this way you can mock it without difficulty :
public class DataTypeFactoryWrapper {
public DatatypeFactory newInstance(){
return DatatypeFactory.newInstance();
}
}
Now change your code in this way :
private DataTypeFactoryWrapper dataTypeFactoryWrapper;
//...
xmlGregorianCalendar = dataTypeFactoryWrapper.newInstance().newXMLGregorianCalendar(dateString);
Now you can mock dataTypeFactoryWrapper
in your test class.
3) Last alternative : don't test it. Consider it as it is, that is an Error
wrapper and Error
are hard/tricky to test.
Whatever the javadoc explains that :
An Error is a subclass of Throwable that indicates serious problems that a reasonable application should not try to catch. Most such errors are abnormal conditions
You can either:
Mock static method (DatatypeFactory.newInstance()) using PowerMock for example and set it up to throw DatatypeConfigurationException. Then in unit test check that this exception is wrapped by ConversionException.
Since I'm not a big fan of mocking static methods - I would create new component - say XmlGregorianCalendarProvider (which will use DatatypeFactory.newInstance().newXMLGregorianCalendar(dateString) internally) and mock it instead using standard mocking mechanism (e.g. JUnit). Still then in unit test check that this exception is wrapped by ConversionException.
I just had to do this for my unit tests. All you need to do is set the Sys prop javax.xml.datatype.DatatypeFactory to something invalid. It will throw the exception when it tries to find the class that doesn't exist. Just be sure to clear the prop after your test.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With