Pour la méthode ci-dessous, existe-t-il un moyen de créer un test unitaire pour provoquer DatatypeConfigurationException , afin que je puisse tester qu'il a lancé ConversionException ?
Voici mon 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;
}
4 Réponses :
Vous pouvez remplacer l'implémentation que la fabrique créera en définissant une propriété système avec un nom de classe à instancier. Ensuite, cette classe peut lancer une exception dans cette méthode.
Par exemple, comme ceci
System.setProperty("javax.xml.datatype.DatatypeFactory", FailingDatatypeFactory.class.getName());
puis la configurer comme ça
public class FailingDatatypeFactory implements DatatypeFactory {
public XMLGregorianCalendar newXMLGregorianCalendar() { throw new DatatypeConfigurationException() }
}
Dans cet exemple, comment vérifieriez-vous dans un test que la méthode newXMLGregorianCalendar a été appelée pour le dateString correct?
Ce code ne se compile pas. L'usine doit mettre en œuvre d'autres méthodes. Notez également qu'il ne se compilera pas pour une autre raison: vous lancez une exception vérifiée qui n'est pas déclarée dans l'interface. Ce n'est pas valable. Enfin, vous stub une exception qui n'est jamais levée par cette méthode elle-même. Il est lancé par DatatypeFactory.newInstance ()
Vous pouvez soit:
Méthode statique simulée (DatatypeFactory.newInstance ()) utilisant PowerMock par exemple et configurez-la pour lancer une exception DatatypeConfigurationException. Ensuite, dans le test unitaire, vérifiez que cette exception est encapsulée par ConversionException.
Puisque je ne suis pas un grand fan des méthodes statiques moqueuses - je créerais un nouveau composant - disons XmlGregorianCalendarProvider (qui utilisera DatatypeFactory.newInstance (). newXMLGregorianCalendar (dateString) en interne) et le moquerais à la place en utilisant la moquerie standard mécanisme (par exemple JUnit). Toujours dans le test unitaire, vérifiez que cette exception est encapsulée par ConversionException.
Ceci est contrôlé et ressemble à un nouveau code. Ma règle d'or est la suivante: n'utilisez jamais Powermock sur un nouveau code car il sent le mauvais code. Se débarrasser de la fabrique de singleton statique en faveur d'une instance d'interface injectable permettrait de tester même sans se moquer (simplement en simulant l'interface).
Oleg - c'est pourquoi je propose l'option 2. La moquerie n'est vraiment pas différente de l'implémentation d'interface personnalisée et est plus lisible.
Bien, j'aurais aimé que l'ordre soit différent, c'est d'abord la meilleure option. Il n'y aurait pas de questions alors :)
Ici, l'exception vérifiée est levée par javax.xml.datatype.DatatypeFactory.newInstance () .
C'est une méthode statique. Vous ne pouvez donc pas vous en moquer franchement.
1) Vous pouvez également essayer de trouver le scénario qui pourrait provoquer la levée de l'exception.
Allons-y. L'exception est lancée ici:
private DataTypeFactoryWrapper dataTypeFactoryWrapper; //... xmlGregorianCalendar = dataTypeFactoryWrapper.newInstance().newXMLGregorianCalendar(dateString);
Donc DatatypeConfigurationException est levée lorsque ServiceConfigurationError est lancé et intercepté. Mais ServiceConfigurationError est une erreur et non une exception.
Essayer de simuler une erreur devient très compliqué.
2) Autre alternative pour le tester: encapsuler DatatypeFactory.newInstance () dans une instance de votre propre classe.
De cette façon, vous pouvez vous en moquer sans difficulté:
public class DataTypeFactoryWrapper {
public DatatypeFactory newInstance(){
return DatatypeFactory.newInstance();
}
}
Maintenant changez votre code de cette façon:
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;
}
}
Maintenant vous peut se moquer de dataTypeFactoryWrapper dans votre classe de test.
3) Dernière alternative: ne la testez pas. Considérez-le tel quel, c'est un wrapper Error et Error sont difficiles / difficiles à tester.
Quoi que le javadoc explique que:
Une erreur est une sous-classe de Throwable qui indique des problèmes graves qu'une application raisonnable ne devrait pas essayer d'attraper. La plupart de ces les erreurs sont des conditions anormales
Je devais juste le faire pour mes tests unitaires. Tout ce que vous avez à faire est de définir la prop Sys javax.xml.datatype.DatatypeFactory sur quelque chose d'invalide. Il lèvera l'exception lorsqu'il essaiera de trouver la classe qui n'existe pas. Assurez-vous simplement de nettoyer l'accessoire après votre test.
J'ai répondu avec un moyen de tester ce code, qu'il en vaille la peine ou non est une question d'opinion et dépend fortement de votre cas particulier.
Il est préférable de ne pas utiliser de singletons mais d'utiliser des instances injectables pour les usines. Si vous aviez suivi que votre test serait trivial, vous injecteriez un simulacre qui lève et une exception lorsqu'il est appelé, et vous pouvez le tester en 3 lignes de code. Et oui, cela vaut la peine de le faire car votre code en aval peut en fait avoir une logique de gestion de
ConversionExceptionet une autre pour un cas générique deRuntimeException.Comme indiqué, pour votre question "vaut-il la peine?" La réponse est non. La gestion des captures est triviale. Cependant, vous devez tester que le code qui appelle getXMLGregorianCalendar gère correctement l'exception.