假设我有两个xml字符串
<test>
<elem>a</elem>
<elem>b</elem>
</test>
<test>
<elem>b</elem>
<elem>a</elem>
</test>
如何编写一个比较这两个字符串并忽略元素顺序的测试?
我希望测试尽可能短,不需要10行XML解析等。我正在寻找一个简单的断言或类似的东西。
我有这个(不起作用)
Diff diff = XMLUnit.compareXML(expectedString, actualString);
XMLAssert.assertXMLEqual("meh", diff, true);
答案 0 :(得分:19)
对于xmlunit 2.0(我一直在寻找这个),现在使用DefaultNodeMatcher
完成了Diff diff = Diffbuilder.compare(Input.fromFile(control))
.withTest(Input.fromFile(test))
.withNodeMatcher(new DefaultNodeMatcher(ElementSelectors.byNameAndText))
.build()
希望这有助于帮助其他人在Google上搜索...
答案 1 :(得分:18)
XMLUnit将执行您想要的操作,但您必须指定elementQualifier。如果未指定elementQualifier,则只会比较相同位置的节点。
对于您想要ElementNameAndTextQualifer的示例,如果存在与节点名称及其文本值匹配的节点,则会考虑类似的节点,如:
Diff diff = new Diff(control, toTest);
// we don't care about ordering
diff.overrideElementQualifier(new ElementNameAndTextQualifier());
XMLAssert.assertXMLEqual(diff, true);
您可以在此处详细了解:http://xmlunit.sourceforge.net/userguide/html/ar01s03.html#ElementQualifier
答案 2 :(得分:9)
我原来的答案已经过时了。如果我必须再次构建它,我将使用xmlunit 2和xmlunit-matchers。请注意,对于xml单元,不同的顺序总是“相似”而不是等于。
@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)));
}
如果有人仍然不想在这里使用纯Java实现,那就是。此实现从xml中提取内容并比较列表忽略顺序。
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()));
}
答案 3 :(得分:1)
从Compare XML ignoring order of child elements
交叉发布今晚我有类似的需求,无法找到符合我要求的东西。
我的解决方法是对我想要的两个XML文件进行排序,按字母顺序按元素名称排序。一旦它们处于一致的顺序,我就可以使用常规的视觉差异工具来区分两个已排序的文件。
如果这种方法听起来对其他人有用,我已经分享了我写的python脚本,以便在http://dalelane.co.uk/blog/?p=3225进行排序
答案 4 :(得分:0)
选项1
如果XML代码很简单,请尝试以下方法:
String testString = ...
assertTrue(testString.matches("(?m)^<test>(\\s*<elem>(a|b)</elem>\\s*){2}</test>$"));
选项2
如果XML更复杂,请使用XML解析器加载它,并将找到的实际节点与参考节点进行比较。
答案 5 :(得分:0)
仅作为如何比较基于属性name
的相等性的更复杂的xml元素匹配的示例。例如:
<request>
<param name="foo" style="" type="xs:int"/>
<param name="Cookie" path="cookie" style="header" type="xs:string" />
</request>
VS
<request>
<param name="Cookie" path="cookie" style="header" type="xs:string" />
<param name="foo" style="query" type="xs:int"/>
</request>
使用以下自定义元素限定符:
final Diff diff = XMLUnit.compareXML(controlXml, testXml);
diff.overrideElementQualifier(new ElementNameAndTextQualifier() {
@Override
public boolean qualifyForComparison(final Element control, final Element test) {
// this condition is copied from super.super class
if (!(control != null && test != null
&& equalsNamespace(control, test)
&& getNonNamespacedNodeName(control).equals(getNonNamespacedNodeName(test)))) {
return false;
}
// matching based on 'name' attribute
if (control.hasAttribute("name") && test.hasAttribute("name")) {
if (control.getAttribute("name").equals(test.getAttribute("name"))) {
return true;
}
}
return false;
}
});
XMLAssert.assertXMLEqual(diff, true);
答案 6 :(得分:0)
对我来说,我还需要在checkForSimilar()
上添加方法:DiffBuilder
。
没有它,断言是错误的,说节点的顺序不一样(子列表中的位置不一样)
我的代码是:
Diff diff = Diffbuilder.compare(Input.fromFile(control))
.withTest(Input.fromFile(test))
.withNodeMatcher(new DefaultNodeMatcher(ElementSelectors.byNameAndText))
.checkForSimilar()
.build()
答案 7 :(得分:0)
我不知道他们为解决方案采用了什么版本,但是没有任何效果(或者至少很简单),所以这是我为那些同样痛苦的人准备的解决方案。
P.S。我讨厌人们错过静态方法的导入或FQN类名
@Test
void given2XMLS_are_different_elements_sequence_with_whitespaces(){
String testXml = "<struct><int>3</int> <boolean>false</boolean> </struct>";
String expected = "<struct><boolean>false</boolean><int>3</int></struct>";
XmlAssert.assertThat(testXml).and(expected)
.ignoreWhitespace()
.normalizeWhitespace()
.withNodeMatcher(new DefaultNodeMatcher(ElementSelectors.byNameAndText))
.areSimilar();
}