1

我需要使用XMLUnit 2xml 子节点的顺序不同的地方来比较 XML 文件。由于使用了底层库,我无法影响子节点的顺序。

为了比较,我正在使用:

    <dependency>
        <groupId>org.xmlunit</groupId>
        <artifactId>xmlunit-core</artifactId>
        <version>2.0.0-alpha-02</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.xmlunit</groupId>
        <artifactId>xmlunit-matchers</artifactId>
        <version>2.0.0-alpha-02</version>
        <scope>test</scope>
    </dependency>

问题归结为这个 JUnit 测试:

import static org.hamcrest.MatcherAssert.assertThat;
import static org.xmlunit.builder.Input.fromString;
import static org.xmlunit.diff.ElementSelectors.byName;
import static org.xmlunit.matchers.CompareMatcher.isSimilarTo;

import org.apache.commons.io.IOUtils;
import org.junit.Test;
import org.springframework.core.io.ClassPathResource;
import org.xmlunit.diff.DefaultNodeMatcher;

import java.io.IOException;

public class XmlTest {

    @Test
    public void test() throws Exception {
        String t1 = fromClassPath("t1.xml");
        String t2 = fromClassPath("t2.xml");
        assertThat(fromString(t1), isSimilarTo(fromString(t2)).withNodeMatcher(new DefaultNodeMatcher(byName)));
    }

    private static String fromClassPath(String fileName) throws IOException {
        return IOUtils.toString(new ClassPathResource(fileName).getInputStream());
    }
}

其中t1.xml包含:

<root>
    <child>
        <node1/>
    </child>
    <child>
        <node2/>
    </child>
</root>

t2.xml包含:

<root>
    <child>
        <node2/>
    </child>
    <child>
        <node1/>                                                                                                                        
    </child>
</root>

实际上,node1并且node2是按顺序更改的。测试结果是:

java.lang.AssertionError: 
Expected: Expected child 'node2' but was 'null' - comparing <node2...> at /root[1]/child[1]/node2[1] to <NULL>:
<node2/>
     but: result was: 
<NULL>  

我想知道是否可以将此类 xml 文件与XMLUnit 2. 任何形式的帮助表示赞赏。

4

1 回答 1

5

您告诉 XMLUnit 按名称匹配元素,无需附加条件。在您的情况下,这意味着child按顺序比较元素。您显然需要一个不同的策略,在决定child选择哪个节点作为任何给定的“伙伴”时查看节点的第一个子节点child

一种选择是

    new DefaultNodeMatcher(byXPath("./*[1]", byName), byName))

这将首先通过尝试匹配节点的第一个子节点来匹配节点,并且 - 如果未能提出任何候选者 - 使用元素本身的名称。

根据您真实文档的结构,这可能过于幼稚,您需要使用条件元素选择器

    new DefaultNodeMatcher(selectorForElementNamed("child", byXPath("./*[1]", byName)), byName))

注意:由于问题 #39,上述代码不适用于 XMLUnit 2.0.0-alpha-02 - 您必须改用 XPath "./child/*[1]"。之后已byXPath ElementSelector修复。

如果您不想使用 XPath,您可以ElementSelector自己编写代码

public static class FirstChildElementNameSelector implements ElementSelector {
    @Override
    public boolean canBeCompared(Element controlElement,
                                 Element testElement) {
        return byName.canBeCompared(firstChildElement(controlElement),
                                    firstChildElement(testElement));
    }

    private Element firstChildElement(Element e) {
        NodeList nl = e.getChildNodes();
        int len = nl.getLength();
        for (int i = 0; i < len; i++) {
            if (nl.item(i) instanceof Element) {
                return (Element) nl.item(i);
            }
        }
        return null;
    }
}

并像使用它一样

    new DefaultNodeMatcher(selectorForElementNamed("child", new FirstChildElementNameSelector()), byName))
于 2015-11-29T07:42:53.150 回答