J'essaie d'écrire des tests unitaires pour diverses opérations de clone () dans un grand projet et je me demande s'il existe une classe existante capable de prendre deux objets du même type, en effectuant une comparaison approfondie. dire s'ils sont identiques ou non?
Réponses
Trop de publicités?J'adore cette question! Principalement parce qu'il n'est presque jamais répondu ou ont répondu mal. C'est comme si personne n'avait pensé à elle encore. Territoire vierge :)
Tout d'abord, ne pas même penser à propos de l'utilisation d' equals
. Le contrat d' equals
, tel que défini dans la javadoc, est une relation d'équivalence (réflexive, symétrique et transitive), pas une relation d'égalité. Pour cela, il devra également être antisymétrique. La seule mise en œuvre d' equals
qui est (ou pourrait être) une vraie relation d'égalité est le seul en java.lang.Object
. Même si vous n'utilisez equals
à comparer tout dans le graphique, le risque de rupture du contrat est très élevée. Comme Josh Bloch l'a souligné dans l' Effectif de Java, le contrat d'égal à égal est très facile à briser:
"Il n'y a tout simplement aucun moyen d'étendre une classe instanciable et ajouter un aspect tout en préservant les égaux contrat"
D'ailleurs a quoi bon un booléen vraiment faire de vous de toute façon? Ce serait bien effectivement de les encapsuler toutes les différences entre l'original et le clone, vous ne pensez pas? Aussi, je vais supposer que vous ne voulez pas être dérangé par la rédaction/le maintien de la comparaison de code pour chaque objet sur le graphique, mais plutôt que vous êtes à la recherche de quelque chose qui va à l'échelle avec la source de leur évolution dans le temps.
Donc, ce que vous voulez vraiment est une sorte d'état de l'outil de comparaison. Comment cet outil est mis en œuvre dépend vraiment de la nature de votre modèle de domaine et de votre performance restrictions. Dans mon expérience, il n'y a pas de générique de la balle magique. Et il va être lent sur un grand nombre d'itérations. Mais pour tester l'intégralité d'une opération de duplication, il va faire le travail assez bien. Vos deux meilleures options sont de sérialisation et de réflexion.
Certains problèmes que vous rencontrerez:
- Collection d'ordre: si deux collections être considérés comme similaires si elles possèdent les mêmes objets, mais dans un ordre différent?
- Les champs à ignorer: Transitoire? Statique?
- Type d'équivalence: si les valeurs de l'être exactement du même type? Ou est-ce ok pour l'une afin d'étendre l'autre?
- Il n'y a plus, mais j'ai oublié...
XStream est assez rapide et combiné avec XMLUnit va faire le travail en seulement quelques lignes de code. XMLUnit est agréable, car il peut faire rapport de toutes les différences, ou simplement s'arrêter à la première qu'il trouve. Et sa sortie inclut le chemin xpath de l'différents nœuds, ce qui est agréable. Par défaut, il ne permet pas non ordonnée des collections, mais il peut être configuré pour le faire. L'injection d'une différence particulière gestionnaire (Appelé" DifferenceListener
) vous permet de spécifier la façon dont vous voulez gérer les différences, y compris en ignorant l'ordre. Cependant, dès que vous voulez faire quelque chose au-delà de la simple personnalisation, il devient difficile d'écrire et les détails ont tendance à être lié à un domaine spécifique de l'objet.
Ma préférence personnelle est d'utiliser la réflexion pour faire défiler tous les déclaré champs et de forage vers le bas dans chacune, suivi de différences que je vais. Mot d'avertissement: Ne pas utiliser la récursivité sauf si vous aimez le débordement de la pile d'exceptions. Garder les choses en portée avec une pile (utilisation d'un LinkedList
ou quelque chose). J'ai l'habitude de les ignorer transitoire et des champs statiques, et je me passer de paires d'objets que j'ai déjà comparé, donc je ne finira pas dans une boucle infinie si quelqu'un a décidé d'écrire auto-référentielle de code (Cependant, j'ai toujours comparer primitive wrappers n'importe quoi, depuis le même objet refs sont souvent réutilisés). Vous pouvez configurer les choses avant de les ignorer collection de commander et de faire abstraction des types spéciaux ou dans les champs, mais j'aime à définir mon état de comparaison des politiques sur les champs eux-mêmes via des annotations. Cela, à mon humble avis, c'est exactement ce que les annotations ont été destinées, de faire des méta-données sur la classe disponible au moment de l'exécution. Quelque chose comme:
@StatePolicy(unordered=true, ignore=false, exactTypesOnly=true)
private List<StringyThing> _mylist;
Je pense que c'est effectivement un problème vraiment difficile, mais tout à fait possible! Et une fois que vous avez quelque chose qui fonctionne pour vous, c'est vraiment, vraiment, à portée de main :)
Donc, bonne chance. Et si vous venez avec quelque chose qui est juste du pur génie, n'oubliez pas de partager!
Apache commons-lang a un constructeur basé sur la réflexion basé sur la réflexion documenté ici: http://commons.apache.org/proper/commons-lang/javadocs/api-3.1/org/apache/commons/lang3/builder/EqualsBuilder.html (corrigé le lien, encore une fois)
Voir DeepEquals et DeepHashCode (): http://code.google.com/p/deep-equals/
Cette classe fait exactement ce que demande l'auteur original.