Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Create unit test for unlikely scenario and is it worth the trouble?

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;
}
like image 371
Angelina Avatar asked Jan 11 '19 15:01

Angelina


4 Answers

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.

like image 143
emilianogc Avatar answered Oct 23 '22 10:10

emilianogc


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

like image 1
davidxxx Avatar answered Oct 23 '22 10:10

davidxxx


You can either:

  1. 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.

  2. 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.

like image 1
oceansize Avatar answered Oct 23 '22 11:10

oceansize


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.

like image 1
matthenry87 Avatar answered Oct 23 '22 11:10

matthenry87