Skip to content

Migrating from XMLUnit 1.x to 2.x

Leaqui edited this page Oct 18, 2018 · 10 revisions

xmlunit-legacy

Part of XMLUnit for Java is a legacy module which contains the XMLUnit 1.x classes but reimplements most of the logic using the XMLUnit 2.x engine. Using xmlunit-legacy is the fastest way to migrate but it is deprecated and will not be extended. New features will only become available in xmlunit-core or xmlunit-matchers.

Changed Defaults between 1.x and 2.x

  • XMLUnit 2.x will never try to compare unmatched nodes with arbitrary other nodes.
  • XMLUnit 2.x will never care for the order of attributes.
  • By default element order is significant in 2.x - use a custom ElementSelector if you want SIMILAR results for documents that differ in element order. The default of XMLUnit 1.x's Diff and DetailedDiff translates to ElementSelectors.byName.
  • XMLUnit 2.x detects differences inside of the document's prolog - XMLUnit 1.x only cared for the doctype declaration. You can mimic this behavior by chaining DifferenceEvaluators.ignorePrologDifferencesExceptDoctype with the DifferenceEvaluator you intended to use (most likely DifferenceEvaluators.Default.

XMLUnit 1.x static Configurations:

XMLUnit 2.x has completely removed all static configuration.

XMLUnit 1.x XMLUnit 2.x
XMLUnit.setIgnoreAttributeOrder() Attribute order is always ignored.
XMLUnit.setCompareUnmatched() Unmatched nodes are never compared to other nodes.
XMLUnit.setIgnoreComments() wrap the input in a CommentLessSource or use DiffBuilder.ignoreComments()
XMLUnit.setIgnoreDiffBetweenTextAndCDATA() TEXT and CDATA are considered ComparisonResult.SIMILAR (any other behaviour would require a custom DifferenceEvaluator)
XMLUnit.setIgnoreWhitespace() wrap the input in a WhitespaceStrippedSource or use DiffBuilder.ignoreWhitespace()
XMLUnit.setNormalizeWhitespace() wrap the input in a WhitespaceNormalizedSource or use DiffBuilder.normalizeWhitespace()

Creating a Diff instance:

Old XMLUnit 1.x Code:

public void testXMLLegacyIdentical()throws Exception {
    String myControlXML =
        "<struct><int>3</int><boolean>false</boolean></struct>";
    String myTestXML =
        "<struct><boolean>false</boolean><int>3</int></struct>";
    Diff myDiff = new Diff(myControlXML, myTestXML);
    assertTrue("XML similar " + myDiff.toString(),
               myDiff.similar());
    assertTrue("XML identical " + myDiff.toString(),
               myDiff.identical());
}

New XMLUnit 2.x Code with xmlunit-core:

public void testXMLCoreIdentical()throws Exception {
    String myControlXML =
        "<struct><int>3</int><boolean>false</boolean></struct>";
    String myTestXML =
        "<struct><boolean>false</boolean><int>3</int></struct>";
		
    Diff myDiffSimilar = DiffBuilder.compare(myControlXML).withTest(myTestXML)
     .withNodeMatcher(new DefaultNodeMatcher(ElementSelectors.byName))
     .checkForSimilar()
     .build();
	assertFalse("XML similar " + myDiffSimilar.toString(), myDiffSimilar.hasDifferences());
	
    Diff myDiffIdentical = DiffBuilder.compare(myControlXML).withTest(myTestXML)
     .withNodeMatcher(new DefaultNodeMatcher(ElementSelectors.byName))
     .checkForIdentical()
     .build();
	assertFalse("XML identical " + myDiffIdentical.toString(), myDiffIdentical.hasDifferences());
}

New XMLUnit 2.x Code with xmlunit-matchers:

public void testXMLMatchersIdentical()throws Exception {
    String myControlXML =
        "<struct><int>3</int><boolean>false</boolean></struct>";
    String myTestXML =
        "<struct><boolean>false</boolean><int>3</int></struct>";
		
	assertThat(myTestXML, CompareMatcher.isSimilarTo(myControlXML));
	assertThat(myTestXML, CompareMatcher.isIdenticalTo(myControlXML));
}

Where XMLUnit 1.x defaults to match elements by name, XMLUnit 2.x defaults to match elements in order. The .withNodeMatcher(new DefaultNodeMatcher(ElementSelectors.byName)) line above is needed to mimic the default behaviour of 1.x.

Old XMLUnit 1.x DetailedDiff

In XmlUnit 2.x there is no DetailedDiff anymore. The new Diff object already contains all differences by default.

Old code:

DetailedDiff myDiff = new DetailedDiff(new Diff(myControlXML, myTestXML));
List allDifferences = myDiff.getAllDifferences();
assertEquals(myDiff.toString(), 2, allDifferences.size());

New Code

Diff myDiff = DiffBuilder.compare(myControlXML).withTest(myTestXML).build();
Iterable<Difference> differences = myDiff.getDifferences();
for (Difference difference : differences) {
	System.out.println(difference);
}

In XMLUnit 1.x the use of Diff implied the comparison would stop as soon as the first real difference was found and you had to use DetailedDiff in order to find all differences.

With XMLUnit 2.x the decision to stop or continue the comparison process is made by an instance of ComparisonController (actually XMLUnit 1.x uses DifferenceListener for this and Diff as well as DetailedDiff implement this).

If you want stop comparision after first difference found, you must set a ComparisonController:

DiffBuilder.withComparisonController(ComparisonControllers.StopWhenDifferent)

Migrate custom XMLUnit 1.x DifferenceListener to the XMLUnit 2.x DifferenceEvaluator

The old DifferenceListener interface...

public interface DifferenceListener {
	...
	int differenceFound(Difference difference);
	...
}

...is replaced by the new DifferenceEvaluator interface:

public interface DifferenceEvaluator {
    ComparisonResult evaluate(Comparison comparison, ComparisonResult outcome);
}

and its role is restricted to changing the severity of a comparison's outcome. Unlike DifferenceListener the new interface doesn't influence whether the comparison process stops - this is the responsibility of the new ComparisonController interface.

changed Features in DifferenceEvaluator

XMLUnit 1.x XMLUnit 2.x
difference.getControlNodeDetail().getNode() comparison.getControlDetails().getTarget()
difference.getControlNodeDetail().getValue() comparison.getControlDetails().getValue()
difference.getControlNodeDetail().getXpathLocation() comparison.getControlDetails().getXPath()
difference.getTestNodeDetail() comparison.getTestDetails()
difference.getDescription() responsibility of a ComparisionFormatter, e.g: new DefaultComparisonFormatter().getDescription(comparison)
difference.isRecoverable() you now get the ComparisonResult enum as separate paramter. If you need check for ComparisonResult.SIMILAR, you must chain your DifferenceEvaluator with the default evaluator (see example below).
difference.getId() is now an enum: comparison.getType()

With XMLUnit 2.x you can chain multiple DifferenceEvaluators:

DifferenceEvaluator chainedEvaluator = DifferenceEvaluators.chain(
             DifferenceEvaluators.Default, new MyDifferenceEvaluator());

DifferenceEvaluators.chain() will pass the outcome from one evaluator to the next evaluator. In this example the MyDifferenceEvaluator can also check if the previous outcome was ComparisonResult.SIMILAR.

DifferenceListener to DifferenceEvaluator migration Example:

DifferenceEvaluator migration

Migrate XMLUnit 1.x ElementQualifier to XMLUnit 2.x ElementSelector

In XMLUnit 2.x the role of the old ElementQualifier has been expanded to the more general NodeMatcher. Its default implementation DefaultNodeMatcher delegates part of its responsibility to ElementSelector and the easiest approach to migrate an ElementQualifier is to implement ElementSelector and use something like

NodeMatcher nodeMatcher = new DefaultNodeMatcher(new MyElementSelector()):

The Interfaces of the old ElementQualifier and the new ElementSelector are very similar:

ElementQualifier

boolean qualifyForComparison(Element control, Element test); 

ElementSelector

boolean canBeCompared(Element controlElement, Element testElement);

ElementQualifier to ElementSelector migration Example:

ElementSelector migration

Clone this wiki locally