DifferenceEvaluator - xmlunit/user-guide GitHub Wiki
A DifferenceEvaluator
decides about the outcome of a comparison. A
couple of convenience implementations have been collected in the
DifferenceEvaluators
class.
DifferenceEvaluators.Default
uses rules very similar to those of
XMLUnit for Java 1.x in order to decide whether a technically
different outcome is ComparisonResult.DIFFERENT
or rather a not so
critical difference that would still allow the pieces of XML to be
considered SIMILAR
.
With the default evaluator the following differences are handled as similar:
- CDATA and Text nodes with the same content
- DOCTYPE differences
- different
xsi:schemaLocation
andxsi:noNamspaceSchemaLocation
- different XML namespaces prefixes
- explicit/implicit status of attributes.
- a different order of child nodes
- XML encoding
If you use your own DifferenceEvaluator
implementation you can
combine it with the default DifferenceEvaluator.
Note
The order of child nodes is consider asSIMILAR
provided theNodeMatcher
selects the correct nodes for comparison. As theDefaultDifferenceEvaluator
usesElementSelectors.Default
for selecting the nodes.
❗ This Example implementations are strongly simplified without exception handling or not-null checks. They should only be a clue what a DifferenceEvaluator can do. ❗
In some cases you want ignore some special differences of an XML content. Typical cases are elements/attributes which are filled with random values like UUIDs or current date/time information.
class IgnoreAttributeDifferenceEvaluator implements DifferenceEvaluator {
private String attributeName;
public IgnoreAttributeDifferenceEvaluator(String attributeName) {
this.attributeName = attributeName;
}
@Override
public ComparisonResult evaluate(Comparison comparison, ComparisonResult outcome) {
if (outcome == ComparisonResult.EQUAL) return outcome; // only evaluate differences.
final Node controlNode = comparison.getControlDetails().getTarget();
if (controlNode instanceof Attr) {
Attr attr = (Attr) controlNode;
if (attr.getName().equals(attributeName)) {
return ComparisonResult.SIMILAR; // will evaluate this difference as similar
}
}
return outcome;
}
}
Usage:
final String control = "<a><b attr=\"abc\"></b></a>";
final String test = "<a><b attr=\"xyz\"></b></a>";
Diff myDiff = DiffBuilder.compare(control).withTest(test)
.withDifferenceEvaluator(new IgnoreAttributeDifferenceEvaluator("attr"))
.checkForSimilar()
.build();
Assert.assertFalse(myDiff.toString(), myDiff.hasDifferences());
for Java or for .NET:
public class IgnoreAttributeDifferenceEvaluator {
private string attributeName;
public IgnoreAttributeDifferenceEvaluator(string attributeName) {
this.attributeName = attributeName;
}
public ComparisonResult Evaluate(Comparison comparison, ComparisonResult outcome) {
if (outcome == ComparisonResult.EQUAL) return outcome; // only evaluate differences.
XmlNode controlNode = comparison.ControlDetails.Target;
XmlAttribute attr = controlNode as XmlAttribute;
if (attr != null) {
if (attr.Name == attributeName) {
return ComparisonResult.SIMILAR; // will evaluate this difference as similar
}
}
return outcome;
}
}
usage
string control = "<a><b attr=\"abc\"></b></a>";
string test = "<a><b attr=\"xyz\"></b></a>";
var myDiff = DiffBuilder.Compare(control).WithTest(test)
.WithDifferenceEvaluator(new IgnoreAttributeDifferenceEvaluator("attr").Evaluate)
.CheckForSimilar()
.Build();
Assert.IsFalse(myDiff.HasDifferences(), myDiff.ToString());
In some cases you don't want compare the content of an element as String.
This example shows how to compare a special element with BigDecimal.
Because for our fictive business case it doesn't mater if the value is "1.0" or "1.00".
class BigDecimalElementDifferenceEvaluator implements DifferenceEvaluator {
private String elementName;
public BigDecimalElementDifferenceEvaluator(String elementName) {
this.elementName = elementName;
}
@Override
public ComparisonResult evaluate(Comparison comparison, ComparisonResult outcome) {
if (outcome == ComparisonResult.EQUAL) return outcome; // only evaluate differences.
final Node controlNode = comparison.getControlDetails().getTarget();
final Node testNode = comparison.getTestDetails().getTarget();
if (controlNode.getParentNode() instanceof Element && testNode.getParentNode() instanceof Element) {
Element controlElement = (Element) controlNode.getParentNode();
Element testElement = (Element) testNode.getParentNode();
if (controlElement.getNodeName().equals(elementName)) {
final String controlValue = controlElement.getTextContent();
final String testValue = testElement.getTextContent();
if (new BigDecimal(controlValue).compareTo(new BigDecimal(testValue)) == 0) {
return ComparisonResult.SIMILAR;
}
}
}
return outcome;
}
}
Usage:
final String control = "<a><amount>1</amount></a>";
final String test = "<a><amount>1.0000</amount></a>";
Diff myDiff = DiffBuilder.compare(control).withTest(test)
.withDifferenceEvaluator(new BigDecimalElementDifferenceEvaluator("amount"))
.checkForSimilar()
.build();
Assert.assertFalse(myDiff.toString(), myDiff.hasDifferences());
If you want use your own DifferenceEvaluator implementation in combination with the default DifferenceEvaluator you must combine them:
Diff myDiff = DiffBuilder.compare(control).withTest(test)
.withDifferenceEvaluator(
DifferenceEvaluators.chain(DifferenceEvaluators.Default,
new MyCustomDifferenceEvaluator()))
.checkForSimilar()
.build();
There are two helper methods to combine DifferenceEvaluators:
-
DifferenceEvaluators.first(...)
Combines multiple DifferenceEvaluators so that the first one that changes the outcome wins. -
DifferenceEvaluators.chain(...)
Combines multiple DifferenceEvaluators so that the result of the first Evaluator will be passed to the next Evaluator.