Search code examples
javaxmlunit-testingxmlunit-2

XMLUnit tag containing name attribute not matching


I am using xmlunit 2.5.0.

Below are my two xml string : One is the controlxml and one is testxml.

String controlXml = "<flowers><flower><f1 name = \"Roses\"/></flower><flower><f1 name = \"Daisy\"/></flower><flower><f1 name = \"Crocus\"/></flower></flowers>";

String testXml = "<flowers><flower><f1 name = \"Daisy\"/></flower><flower><f1 name = \"Roses\"/></flower><flower><f1 name = \"Crocus\"/></flower></flowers>";

Here i am comparing these two string xmls using xmlunit.

My java code is:

org.xmlunit.diff.Diff myDiff = DiffBuilder.compare(controlXml).withTest(testXml)
                    .checkForSimilar() 
                    .withNodeMatcher(new 
                     DefaultNodeMatcher(ElementSelectors.conditionalBuilder()
                               .whenElementIsNamed("f1")
                               .thenUse(ElementSelectors.byName)
                               .elseUse(ElementSelectors.byNameAndText)
                               .build()))
                    .build();

Getting error:

***********************
Expected attribute value 'Roses' but was 'Daisy' - comparing <f1 name="Roses"...> at /flowers[1]/flower[1]/f1[1]/@name to <f1 name="Daisy"...> at /flowers[1]/flower[1]/f1[1]/@name (DIFFERENT)
***********************
***********************
Expected attribute value 'Daisy' but was 'Roses' - comparing <f1 name="Daisy"...> at /flowers[1]/flower[2]/f1[1]/@name to <f1 name="Roses"...> at /flowers[1]/flower[2]/f1[1]/@name (DIFFERENT)
***********************

I want to get no error since Rose is present in the testxml. Why is Rose in controlxml is being compared to Daisy in testxml even though i have rose in the testxml and i have ElementSelectors.byName and whenElementIsNamed("f1").

Which ElementSelector should I use?


Solution

  • Your

    .whenElementIsNamed("f1")
    .thenUse(ElementSelectors.byName)
    

    means your "f1" elements will be in compared in document order. They all have the same name after all. You obviously feel the value of the name attribute is what identifies the "f1"s to compare. If the "f1" would be siblings then a simple

    .thenUse(ElementSelectors.byNameAndAttributes("name"))
    

    should work. byNameAndAttributes takes a varargs list of attributes whose values must match in addition to the element name.

    Unfortunately they are not siblings and you must tell XMLUnit which "flower" elements to pick when traversing the tree. When XMLUnit picks up the wrong "flower"s, it is never going to see the "f1" you consider the same.

    The full solution for your example is

    .withNodeMatcher(new DefaultNodeMatcher(ElementSelectors.conditionalBuilder()
        .whenElementIsNamed("flower")
        .thenUse(ElementSelectors.byXPath("./f1", ElementSelectors.byNameAndAttributes("name")))
        .elseUse(ElementSelectors.byNameAndText)
        .build()))