Search code examples
javaunit-testingjunitxmlunit

Compare two XML strings ignoring element order


Suppose I have two xml strings

<test>
  <elem>a</elem>
  <elem>b</elem>
</test>

<test>
  <elem>b</elem>
  <elem>a</elem>
</test>

How to write a test that compares those two strings and ignores the element order?

I want the test to be as short as possible, no place for 10-line XML parsing etc. I'm looking for a simple assertion or something similar.

I have this (which doesn't work)

   Diff diff = XMLUnit.compareXML(expectedString, actualString);   
   XMLAssert.assertXMLEqual("meh", diff, true);

Solution

  • My original answer is outdated. If I would have to build it again i would use xmlunit 2 and xmlunit-matchers. Please note that for xml unit a different order is always 'similar' not equals.

    @Test
    public void testXmlUnit() {
        String myControlXML = "<test><elem>a</elem><elem>b</elem></test>";
        String expected = "<test><elem>b</elem><elem>a</elem></test>";
        assertThat(myControlXML, isSimilarTo(expected)
                .withNodeMatcher(new DefaultNodeMatcher(ElementSelectors.byNameAndText)));
        //In case you wan't to ignore whitespaces add ignoreWhitespace().normalizeWhitespace()
        assertThat(myControlXML, isSimilarTo(expected)
            .ignoreWhitespace()
            .normalizeWhitespace()
            .withNodeMatcher(new DefaultNodeMatcher(ElementSelectors.byNameAndText)));
    }  
    

    If somebody still want't to use a pure java implementation here it is. This implementation extracts the content from xml and compares the list ignoring order.

    public static Document loadXMLFromString(String xml) throws Exception {
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        DocumentBuilder builder = factory.newDocumentBuilder();
        InputSource is = new InputSource(new StringReader(xml));
        return builder.parse(is);
    }
    
    @Test
    public void test() throws Exception {
        Document doc = loadXMLFromString("<test>\n" +
                "  <elem>b</elem>\n" +
                "  <elem>a</elem>\n" +
                "</test>");
        XPathFactory xPathfactory = XPathFactory.newInstance();
        XPath xpath = xPathfactory.newXPath();
        XPathExpression expr = xpath.compile("//test//elem");
        NodeList all = (NodeList) expr.evaluate(doc, XPathConstants.NODESET);
        List<String> values = new ArrayList<>();
        if (all != null && all.getLength() > 0) {
            for (int i = 0; i < all.getLength(); i++) {
                values.add(all.item(i).getTextContent());
            }
        }
        Set<String> expected = new HashSet<>(Arrays.asList("a", "b"));
        assertThat("List equality without order",
                values, containsInAnyOrder(expected.toArray()));
    }