XmlUnit教程与实例

834 阅读5分钟

学习使用 XmlUnit 2.x测试和验证XML文档的简单易懂的例子。XmlUnit要求我们准确地知道我们要验证的XML内容与一个给定的测试XML进行比较。

对于断言,我们使用Hamcrest匹配器库

目录

  1. 1.Maven的依赖性
  2. 2.读取和比较XML源
  3. 3.使用XmlUnit的比较实例
  4. 4.规范化的XMLs
  5. 5.总结

1.Maven的依赖性

将最新版本的xmlunit-corexmlunit-matchers依赖项纳入项目。

<dependency>
    <groupId>org.xmlunit</groupId>
    <artifactId>xmlunit-core</artifactId>
    <version>2.9.0</version>
</dependency>

<dependency>
    <groupId>org.xmlunit</groupId>
    <artifactId>xmlunit-matchers</artifactId>
    <version>2.9.0</version>
</dependency>

<!-- If using Hamcrest matchers -->
<dependency>
    <groupId>org.hamcrest</groupId>
    <artifactId>hamcrest</artifactId>
    <version>2.2</version>
</dependency>

2.读取和比较XML源

2.1.读取XML源

为了进行比较,XmlUnitCompareMatcher ,可以从各种来源获取输入,如。

  • java.lang.String
  • java.io.File
  • java.io.InputStream
  • java.io.Reader
  • byte array
  • org.w3c.dom.Document 等。

下面的代码演示了用不同的方式来读取一个文件widget.xml

@Test
void createSource_ThenSuccess() throws ParserConfigurationException, IOException, SAXException {
    //1 - Using File
    Input.from(new File("widget.xml"));
    Input.fromFile("widget.xml");
    Input.fromFile(getClass().getClassLoader().getResource("widget.xml").getPath());

    //2 - Using String
    Input.from("<widget><id>1</id><name>demo</name></widget>");
    Input.fromString("<widget><id>1</id><name>demo</name></widget>");

    //3 - Using Stream
    Input.from(XmlUnitExamples.class.getResourceAsStream("widget.xml"));
    Input.fromStream(XmlUnitExamples.class.getResourceAsStream("widget.xml"));

    //4 - From Reader
    Input.from(new StringReader("<widget><id>1</id><name>demo</name></widget>"));
    Input.fromReader(new StringReader("<widget><id>1</id><name>demo</name></widget>"));

    //5 - From Byte Array
    Input.from("<widget><id>1</id><name>demo</name></widget>".getBytes());
    Input.fromByteArray("<widget><id>1</id><name>demo</name></widget>".getBytes());

    //6 - From Document
    DocumentBuilder b = DocumentBuilderFactory.newInstance().newDocumentBuilder();
    Document document = b.parse(new File("widget.xml"));
    Input.from(document);
    Input.fromDocument(document);
}

2.2.比较XML来源

我们可以用各种方式比较两个XML。在下面的例子中,我们正在比较两个XML字符串

String testXml = "<widget><id>1</id><name>demo</name></widget>";
String controlXml = "<widget><id>1</id><name>demo</name></widget>";

assertThat(testXml, isIdenticalTo(identicalXml));

同样地,在下一个例子中,我们正在比较两个XML文件

assertThat(new File("widget.xml"), isIdenticalTo(identicalXml));
assertThat(new File("widget.xml"), isIdenticalTo(new File("otherFile.xml")));

我们可以在比较中 混合不同类型的源

assertThat(new File("widget.xml"), isIdenticalTo(Input.from(identicalXml)));

3.使用XmlUnit的比较实例

让我们检查一下使用XmlUnit比较两个XML的不同方法。

3.1.比较相同的XMLs

当两个XML具有完全相同的内容时,即两个XML具有相同的标签、相同的顺序和相同的值时,它们被认为是相同的。

下面的例子使用CompareMatcher静态方法来比较两个XMLs。

@Test
void compareIdenticalAndSimilarXmlWithHamcrest_ThenSuccess() throws Exception {
    String testXml = "<widget><id>1</id><name>demo</name></widget>";
    String identicalXml = "<widget><id>1</id><name>demo</name></widget>";
    String nonIdenticalXml = "<widget><name>demo</name><id>1</id></widget>";

    assertThat(testXml, isIdenticalTo(identicalXml));
    assertThat(testXml, not(isIdenticalTo(nonIdenticalXml)));
}

我们还可以DiffBuilder API,收集两个XML之间的所有差异。DiffBuilder是一个流畅的API,可以以一种更可控的方式获得差异。

@Test
void compareIdenticalAndSimilarXmlDiff_ThenSuccess() throws Exception {
    String testXml = "<widget><id>1</id><name>demo</name></widget>";
    String identicalXml = "<widget><id>1</id><name>demo</name></widget>";

    Diff diffForIdentical = DiffBuilder
        .compare(testXml)
        .withTest(identicalXml)
        .checkForIdentical()
        .build();

    assertThat(IterableUtils.size(diffForIdentical.getDifferences()), equalTo(0));
}

3.2.比较相似的XMLs

当两个XML具有相同的标签和值,但标签的顺序不同时,就可以认为它们是相似的。在下面的例子中,nonIdenticalXml有类似的标签,但是idname的顺序不同。

默认情况下,XmlUnit使用 DefaultNodeMatcher匹配从根标签开始的相同深度的XML标签,并以相同的顺序。要让XmlUnit忽略这个顺序,我们使用 ElementSelectors.byName在开始匹配之前,按名称对所有标签进行相同深度的排序。

@Test
void compareIdenticalAndSimilarXmlWithHamcrest_ThenSuccess() throws Exception {
    String testXml = "<widget><id>1</id><name>demo</name></widget>";
    String nonIdenticalXml = "<widget><name>demo</name><id>1</id></widget>";

    assertThat(testXml, isSimilarTo(nonIdenticalXml)
        .withNodeMatcher(new DefaultNodeMatcher(ElementSelectors.byName)));
}

类似地,我们可以使用ElementSelectors.byNameDiffBuilderAPI。

@Test
void compareIdenticalAndSimilarXmlDiff_ThenSuccess() throws Exception {
    String testXml = "<widget><id>1</id><name>demo</name></widget>";
    String nonIdenticalXml = "<widget><name>demo</name><id>1</id></widget>";

    Diff diffForSimilarity = DiffBuilder
        .compare(testXml)
        .withTest(nonIdenticalXml)
        .checkForSimilar()
        .withNodeMatcher(new DefaultNodeMatcher(ElementSelectors.byName))
        .build();

    assertThat(IterableUtils.size(diffForSimilarity.getDifferences()), equalTo(0));
}

3.3.比较或忽略特定标签

在匹配XML时,可能需要忽略特定的标签或只比较几个特定的标签。我们可以通过以下方法来实现 ***withNodeFilter()***方法来实现。这个方法将为XML中的每个节点被调用。如果该方法返回true,那么将进行比较,否则跳过。

在下面的XML中,只有id标签将被比较。name标签将被跳过。我们可以通过在*withNodeFilter()*方法中创建一个复杂的表达式来包括或排除任何数量的标签。

@Test
void compareOnlySpecificTags_ThenSuccess() {
    String testXml = "<widget><id>1</id><name>test</name></widget>";
    String otherXml = "<widget><name>live</name><id>1</id></widget>";

    Diff diffs = DiffBuilder.compare(testXml)
        .withTest(otherXml)
        .withNodeMatcher(new DefaultNodeMatcher(ElementSelectors.byName))
        .withNodeFilter(
            node -> node.getNodeName().equalsIgnoreCase("id")
        )
        .build();

    assertThat(IterableUtils.size(diffs.getDifferences()), equalTo(0));
}

3.4.使用XPaths进行比较

在所有情况下,比较整个XML是不可行的。如果我们只想比较XML的一个特定部分,我们可以使用XPath表达式

@Test
void containsAsChildXmlUsingXPath_ThenSuccess() throws Exception {
    String fullXml = "<widget><id>1</id><name>demo</name></widget>";

    //Contains 'id' tag
    assertThat(fullXml, HasXPathMatcher.hasXPath("/widget/id"));   

    //Compare value of 'id' tag
    assertThat(fullXml, EvaluateXPathMatcher.hasXPath("/widget/id/text()", equalTo("1")));
}

4.规范化的XMLs

收到的XML可能并不总是规范化和干净的。我们可以使用两种方法对XML进行规范化。

4.1.使用源码装饰器

XmlUnit提供了以下装饰器,我们可以在第一节中与Source实例构建一起使用。

  • CommentLessSource:提供由原始源的内容组成的XML,并删除所有的注释。
  • WhitespaceStrippedSource: 删除所有的空文本节点并修剪剩余的文本节点。有效地,它从XML中删除了所有的空字符串,包括节点内容。
  • WhitespaceNormalizedSource替换:用Space字符替换文本节点中发现的所有空白字符,并将连续的空白字符折叠成一个Space。
  • NormalizedSource:相邻的文本节点被合并为单个节点,并删除空的文本节点(递归)。
  • ElementContentWhitespaceStrippedSource空白:删除所有仅由空白组成的文本节点。它删除了所有的 "元素内容空白",即XML元素之间的文本内容,这只是 "漂亮打印 "XML的一个伪装。
@Test
void normalizedXmlSources_ThenSuccess() throws Exception {
    CommentLessSource commentLessSource
        = new CommentLessSource(Input.fromFile("widget.xml").build());

    WhitespaceStrippedSource whitespaceStrippedSource
        = new WhitespaceStrippedSource(Input.fromFile("widget.xml").build());

    WhitespaceNormalizedSource whitespaceNormalizedSource
        = new WhitespaceNormalizedSource(Input.fromFile("widget.xml").build());

    ElementContentWhitespaceStrippedSource elementContentWhitespaceStrippedSource
        = new ElementContentWhitespaceStrippedSource(Input.fromFile("widget.xml").build());
}

4.2.使用CompareMatcher

另一种规范化XML的方法是使用CompareMatcher类提供的工厂方法,同时比较两种XML内容。

@Test
void normalizedXmlMatchers_ThenSuccess() throws Exception {
    String testXml = "<widget><id>1</id><name>demo</name></widget>";
    String identicalXml = "<widget><id>1</id><name>demo</name></widget>";

    assertThat(testXml, isIdenticalTo(identicalXml).normalizeWhitespace());
    assertThat(testXml, isIdenticalTo(identicalXml).ignoreComments());
}

5.总结

在这个XmlUnit 2.x教程中,我们学会了创建不同类型的XML源,用装饰器进行规范化,用XPath比较相同、相似和片段。

祝你学习愉快!!。

源代码在Github上

这个帖子有帮助吗?

如果你喜欢这篇文章,请告诉我们。这是我们改进的唯一方法。

没有

相关帖子。

  1. 用Java读写CSV文件--OpenCSV教程
  2. 如何读写CSV文件--超级CSV教程
  3. RxJava教程
  4. Docker教程与Hello World实例
  5. JMeter教程
  6. JSONassert指南(附实例)