Aujourd'hui, vous l'aurez compris, on cause XML. Nombreux sont les systèmes d'informations qui exploitent ce format pour échanger des données ou les structurer. XML étant assez souple dans sa représentation, il peut être difficile de valider dans les tests un résultat obtenu par rapport à un résultat attendu.
Bien sûr, la validation au regard du schéma permet de faire une validation structurelle, mais à ce moment là c'est votre marshaller et son intégration que vous testez et non votre application à proprement parler. Si c'est le contenu et non la structure qui doit être vérifié, quelles sont les options?
Vous avez à disposition les moyens "d'unmarshaller" le XML et vous pouvez procéder à la vérification par rapport aux objets du modèle. Pas mal, mais si c'est une grappe complète d'objet que vous devez confronter dans un mode "result vs expected", il va y avoir de la ligne de code et pas qu'un peu!
Vous pouvez tenter de traiter le flux de façon non structurée, sans tenir compte du XML (et encore moins des outils associés) et en se référant aux caractères. Ne riez pas, je l'ai vu. Et bien entendu, le moindre changement d'encodage ou de préfix d'espace de nom rend votre test caduque.
Vous pouvez créer un extracteur basé sur des expressions XPath et les comparer une à une. En fonction de la complexité et du volume du graphe à vérifier, même remarque que pour la première option, vous allez pisser de la ligne.
XMLUnit est une bibliothèque qui permet de procéder à des comparaisons de documents XML. Ce n'est pas un projet Github (Sourceforge), le site est moche et n'utilise pas Twitter Bootstrap, l'API n'est pas spécialement "fluent" et elle n'est pas nouvelle. Le score hype est au plus bas (jeu vous le vends bien hein?), mais il fait le job avec un minimum d'effort. Je vais éviter de paraphraser la documentation, je vous invite évidemment à vous y référer, et tenter de simplement zoomer sur les points qui ont retenu mon attention.
La base (noter l'espace sur le deuxième arbre):
assertXMLEqual("<root><value>test</value></root>",
"<root ><value>test</value></root>");
Revenons sur la première option: le projet comprend une couche qui permet d'intégrer le document dans le modèle métier. Mieux, les méthodes equals sont implémentées de façon à prendre en compte l'intégralité du graphe contenu. Du coup la comparaison en est simplifiée et en plus c'est le code de prod qui permet de faire la vérification et le test est typé métier, évidemment s'il n'y a pas match vous ne saurez pas d'où ça vient.
Imaginons maintenant qu'une partie des valeurs du graphe soit purement technique et difficilement reproductible, telle qu'un timestamp indiquant la date de génération, exit l'utilisation d'equals et il est temps de se résigner à pisser du code.
C'est là qu'XMLUnit apporte un plus: il est possible d'avoir le contrôle sur les différences relevées par le moteur grâce à des listeners:
final DifferenceListener dl = new DifferenceListener() {
@Override
public int differenceFound(Difference difference) {
if(difference.getControlNodeDetail() == null ||
difference.getControlNodeDetail().getNode() == null ||
difference.getControlNodeDetail().getNode().getParentNode() == null)
return RETURN_ACCEPT_DIFFERENCE;
else if (difference.getControlNodeDetail().getNode().getParentNode().getNodeName().equals("timestamp"))
return RETURN_IGNORE_DIFFERENCE_NODES_SIMILAR;
return RETURN_ACCEPT_DIFFERENCE;
}
@Override
public void skippedComparison(Node control, Node test) { }
};
final Diff diff = new Diff(control, shouldBeSimilar);
diff.overrideDifferenceListener(dl);
assertXMLEqual(diff, true);
Je vous ai préparé un Gist pour illustrer l'utilisation:
https://gist.github.com/bleporini/631581