对于下面的方法,有没有一种方法可以创建单元测试来导致DatatypeConfigurationException
,这样我就可以测试它是否抛出了ConversionException
?
这是我的代码:
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;
}
您可以通过设置具有要实例化的类名的系统属性来覆盖工厂将创建的实现。然后该类可以在该方法中引发异常。
例如像这个
public class FailingDatatypeFactory implements DatatypeFactory {
public XMLGregorianCalendar newXMLGregorianCalendar() { throw new DatatypeConfigurationException() }
}
然后像这样设置
System.setProperty("javax.xml.datatype.DatatypeFactory", FailingDatatypeFactory.class.getName());
现在,在运行测试用例后,应该清除该属性,这样就不会有其他测试试图实例化该实现。
我只需要为我的单元测试做这件事。您所需要做的就是将Sys-prop javax.xml.datatype.DatatypeFactory设置为无效值。当它试图查找不存在的类时,它将抛出异常。测试后一定要清理道具。
这里检查的异常是由javax.xml.datatype.DatatypeFactory.newInstance()
抛出的
这是一种静态方法。所以你不能直接嘲笑它。
1) 作为替代方案,您可以尝试找到可能引发异常上升的场景
我们走吧。这里抛出了一个例外:
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;
}
}
所以当ServiceConfigurationError
被抛出并被捕获时,DatatypeConfigurationException
被抛出。但ServiceConfigurationError
是一个错误,而不是一个例外
试图模拟错误变得非常棘手。
2) 测试它的另一种选择是:将DatatypeFactory.newInstance()
封装在自己类的实例中
这样你就可以毫无困难地嘲笑它:
public class DataTypeFactoryWrapper {
public DatatypeFactory newInstance(){
return DatatypeFactory.newInstance();
}
}
现在用这种方式更改您的代码:
private DataTypeFactoryWrapper dataTypeFactoryWrapper;
//...
xmlGregorianCalendar = dataTypeFactoryWrapper.newInstance().newXMLGregorianCalendar(dateString);
现在,您可以在测试类中模拟dataTypeFactoryWrapper
。
3) 最后一种选择:不要测试它。按原样考虑它,即Error
包装器和Error
很难测试
无论javadoc解释什么:
Error是Throwable的一个子类,表示存在严重问题一个合理的应用程序不应该试图捕捉。大多数这样的错误是的异常情况
您可以:
-
Mock静态方法(DatatypeFactory.newInstance())使用PowerMock,并将其设置为抛出DatatypeConfigurationException。然后在单元测试中检查ConversionException是否包装了此异常。
-
由于我不太喜欢模拟静态方法,我会创建新的组件,比如XmlGregorianCalendarProvider(它将在内部使用DatatypeFactory.newInstance().newXMLGregorianLendar(dateString)),并使用标准的模拟机制(例如JUnit)来模拟它。然后在单元测试中检查ConversionException是否包装了此异常。