Introduction To XMLUnit 2.x – XMLUnit 2.x简介

最后修改: 2016年 7月 27日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

XMLUnit 2.x is a powerful library that helps us test and verify XML content, and comes in particularly handy when we know exactly what that XML should contain.

XMLUnit 2.x是一个强大的库,可以帮助我们测试和验证XML内容,当我们确切地知道该XML应包含哪些内容时,就会特别方便。

And so we’ll mainly be using XMLUnit inside unit tests to verify that what we have is valid XML, that it contains certain information or conforms to a certain style document.

因此,我们将主要在单元测试中使用XMLUnit,验证我们所拥有的是有效的XML,它包含某些信息或符合某种风格的文档。

Additionally, with XMLUnit, we have control over what kind of difference is important to us and which part of the style reference to compare with which part of your comparison XML.

此外,通过XMLUnit,我们可以控制什么样的差异对我们来说是重要的,以及将样式参考的哪一部分与你的比较XML的哪一部分进行比较。

Since we are focusing on XMLUnit 2.x and not XMLUnit 1.x, whenever we use the word XMLUnit, we are strictly referring to 2.x.

由于我们关注的是XMLUnit 2.x而不是XMLUnit 1.x,所以只要我们使用XMLUnit这个词,我们就严格指的是2.x。

Finally, we’ll also be using Hamcrest matchers for assertions, so it’s a good idea to brush up on Hamcrest in case you are not familiar with it.

最后,我们还将在断言中使用Hamcrest匹配器,因此,如果你不熟悉Hamcrest,最好先学习一下Hamcrest

2. XMLUnit Maven Setup

2.XMLUnit Maven设置

To use the library in our maven projects, we need to have the following dependencies in pom.xml:

要在我们的maven项目中使用该库,我们需要在pom.xml中设置以下依赖项。

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

The latest version of xmlunit-core can be found by following this link. And:

最新版本的 xmlunit-core可以通过这个链接找到。还有。

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

The latest version of xmlunit-matchers is available at this link.

最新版本的xmlunit-matchers可以在这个链接上找到。

3. Comparing XML

3.比较XML

3.1. Simple Difference Examples

3.1.简单的差异实例

Let’s assume we have two pieces of XML. They are deemed to be identical when the content and sequence of the nodes in the documents are exactly the same, so the following test will pass:

让我们假设我们有两块XML。当文档中的节点内容和顺序完全相同时,它们被认为是相同的,所以下面的测试将通过。

@Test
public void given2XMLS_whenIdentical_thenCorrect() {
    String controlXml = "<struct><int>3</int><boolean>false</boolean></struct>";
    String testXml = "<struct><int>3</int><boolean>false</boolean></struct>";
    assertThat(testXml, CompareMatcher.isIdenticalTo(controlXml));
}

This next test fails as the two pieces of XML are similar but not identical as their nodes occur in a different sequence:

接下来的测试失败了,因为这两块XML是相似的,但不完全相同,因为它们的节点出现的顺序不同

@Test
public void given2XMLSWithSimilarNodesButDifferentSequence_whenNotIdentical_thenCorrect() {
    String controlXml = "<struct><int>3</int><boolean>false</boolean></struct>";
    String testXml = "<struct><boolean>false</boolean><int>3</int></struct>";
    assertThat(testXml, assertThat(testXml, not(isIdenticalTo(controlXml)));
}

3.2. Detailed Difference Example

3.2.详细的差异实例

Differences between two XML documents above are detected by the Difference Engine.

上述两个XML文档之间的差异是由差异引擎检测的。

By default and for efficiency reasons, it stops the comparison process as soon as the first difference is found.

默认情况下,出于效率的考虑,一旦发现第一个差异,它就会停止比较过程。

To get all the differences between two pieces of XML we use an instance of the Diff class like so:

为了获得两片XML之间的所有差异,我们使用Diff类的一个实例,像这样。

@Test
public void given2XMLS_whenGeneratesDifferences_thenCorrect(){
    String controlXml = "<struct><int>3</int><boolean>false</boolean></struct>";
    String testXml = "<struct><boolean>false</boolean><int>3</int></struct>";
    Diff myDiff = DiffBuilder.compare(controlXml).withTest(testXml).build();
    
    Iterator<Difference> iter = myDiff.getDifferences().iterator();
    int size = 0;
    while (iter.hasNext()) {
        iter.next().toString();
        size++;
    }
    assertThat(size, greaterThan(1));
}

If we print the values returned in the while loop, the result is as below:

如果我们打印while循环中返回的值,结果如下。

Expected element tag name 'int' but was 'boolean' - 
  comparing <int...> at /struct[1]/int[1] to <boolean...> 
    at /struct[1]/boolean[1] (DIFFERENT)
Expected text value '3' but was 'false' - 
  comparing <int ...>3</int> at /struct[1]/int[1]/text()[1] to 
    <boolean ...>false</boolean> at /struct[1]/boolean[1]/text()[1] (DIFFERENT)
Expected element tag name 'boolean' but was 'int' - 
  comparing <boolean...> at /struct[1]/boolean[1] 
    to <int...> at /struct[1]/int[1] (DIFFERENT)
Expected text value 'false' but was '3' - 
  comparing <boolean ...>false</boolean> at /struct[1]/boolean[1]/text()[1] 
    to <int ...>3</int> at /struct[1]/int[1]/text()[1] (DIFFERENT)

Each instance describes both the type of difference found between a control node and test node and the detail of those nodes (including the XPath location of each node).

每个实例都描述了在控制节点和测试节点之间发现的差异类型以及这些节点的细节(包括每个节点的XPath位置)。

If we want to force the Difference Engine to stop after the first difference is found and not proceed to enumerate further differences – we need to supply a ComparisonController:

如果我们想强制Difference Engine在找到第一个差异后停止,并且不继续列举更多的差异–我们需要提供一个ComparisonController

@Test
public void given2XMLS_whenGeneratesOneDifference_thenCorrect(){
    String myControlXML = "<struct><int>3</int><boolean>false</boolean></struct>";
    String myTestXML = "<struct><boolean>false</boolean><int>3</int></struct>";
    
    Diff myDiff = DiffBuilder
      .compare(myControlXML)
      .withTest(myTestXML)
      .withComparisonController(ComparisonControllers.StopWhenDifferent)
       .build();
    
    Iterator<Difference> iter = myDiff.getDifferences().iterator();
    int size = 0;
    while (iter.hasNext()) {
        iter.next().toString();
        size++;
    }
    assertThat(size, equalTo(1));
}

The difference message is simpler:

差异信息更简单:

Expected element tag name 'int' but was 'boolean' - 
  comparing <int...> at /struct[1]/int[1] 
    to <boolean...> at /struct[1]/boolean[1] (DIFFERENT)

4. Input Sources

4.输入源

With XMLUnit, we can pick XML data from a variety of sources that may be convenient for our application’s needs. In this case, we use the Input class with its array of static methods.

通过XMLUnit我们可以从各种来源中挑选XML数据,这些来源可能对我们的应用程序的需求很方便。在这种情况下,我们使用Input类及其静态方法阵列。

To pick input from an XML file located in the project root, we do the following:

要从位于项目根部的XML文件中提取输入,我们要做以下工作。

@Test
public void givenFileSource_whenAbleToInput_thenCorrect() {
    ClassLoader classLoader = getClass().getClassLoader();
    String testPath = classLoader.getResource("test.xml").getPath();
    String controlPath = classLoader.getResource("control.xml").getPath();
    
    assertThat(
      Input.fromFile(testPath), isSimilarTo(Input.fromFile(controlPath)));
}

To pick an input source from an XML string, like so:

要从一个XML字符串中挑选一个输入源,像这样。

@Test
public void givenStringSource_whenAbleToInput_thenCorrect() {
    String controlXml = "<struct><int>3</int><boolean>false</boolean></struct>";
    String testXml = "<struct><int>3</int><boolean>false</boolean></struct>";
    
    assertThat(
      Input.fromString(testXml),isSimilarTo(Input.fromString(controlXml)));
}

Let’s now use a stream as the input:

现在让我们用一个流作为输入。

@Test
public void givenStreamAsSource_whenAbleToInput_thenCorrect() {
    assertThat(Input.fromStream(XMLUnitTests.class
      .getResourceAsStream("/test.xml")),
        isSimilarTo(
          Input.fromStream(XMLUnitTests.class
            .getResourceAsStream("/control.xml"))));
}

We could also use Input.from(Object) where we pass in any valid source to be resolved by XMLUnit.

我们也可以使用Input.from(Object),在这里我们传入任何有效的源,由XMLUnit来解决。

For example, we can pass a file in:

例如,我们可以传入一个文件。

@Test
public void givenFileSourceAsObject_whenAbleToInput_thenCorrect() {
    ClassLoader classLoader = getClass().getClassLoader();
    
    assertThat(
      Input.from(new File(classLoader.getResource("test.xml").getFile())), 
      isSimilarTo(Input.from(new File(classLoader.getResource("control.xml").getFile()))));
}

Or a String:

或一个字符串:

@Test
public void givenStringSourceAsObject_whenAbleToInput_thenCorrect() {
    assertThat(
      Input.from("<struct><int>3</int><boolean>false</boolean></struct>"),
      isSimilarTo(Input.from("<struct><int>3</int><boolean>false</boolean></struct>")));
}

Or a Stream:

或一个流:

@Test
public void givenStreamAsObject_whenAbleToInput_thenCorrect() {
    assertThat(
      Input.from(XMLUnitTest.class.getResourceAsStream("/test.xml")), 
      isSimilarTo(Input.from(XMLUnitTest.class.getResourceAsStream("/control.xml"))));
}

and they will all be resolved.

而且它们都将得到解决。

5. Comparing Specific Nodes

5.比较特定节点

In section 2 above, we only looked at identical XML because similar XML needs a little bit of customization using features from xmlunit-core library:

在上面的第2节中,我们只看了相同的XML,因为类似的XML需要使用xmlunit-core库的功能进行一点点的定制。

@Test
public void given2XMLS_whenSimilar_thenCorrect() {
    String controlXml = "<struct><int>3</int><boolean>false</boolean></struct>";
    String testXml = "<struct><boolean>false</boolean><int>3</int></struct>";
    
    assertThat(testXml, isSimilarTo(controlXml));
}

The above test should pass since the XMLs have similar nodes, however, it fails. This is because XMLUnit compares control and test nodes at the same depth relative to the root node.

上面的测试应该通过,因为XML有类似的节点,然而,它失败了。这是因为XMLUnit在相对于根节点的相同深度比较控制和测试节点

So an isSimilarTo condition is a little bit more interesting to test than an isIdenticalTo condition. The node <int>3</int> in controlXml will be compared with <boolean>false</boolean> in testXml, automatically giving failure message:

所以一个isSimilarTo条件比一个isIdenticalTo条件更值得测试。controlXml中的节点<int>3</int>将与testXml中的<boolean>false进行比较,自动给出失败信息。

java.lang.AssertionError: 
Expected: Expected element tag name 'int' but was 'boolean' - 
  comparing <int...> at /struct[1]/int[1] to <boolean...> at /struct[1]/boolean[1]:
<int>3</int>
   but: result was: 
<boolean>false</boolean>

This is where the DefaultNodeMatcher and ElementSelector classes of XMLUnit come in handy

这就是XMLUnit的DefaultNodeMatcherElementSelector类有用的地方。

The DefaultNodeMatcher class is consulted by XMLUnit at comparison stage as it loops over nodes of controlXml, to determine which XML node from testXml to compare with the current XML node it encounters in controlXml.

XMLUnit在比较阶段会参考DefaultNodeMatcher类,因为它在controlXml的节点上循环,以确定将testXml中的哪个XML节点与controlXml中遇到的当前XML节点进行比较。

Before that, DefaultNodeMatcher will have already consulted ElementSelector to decide how to match nodes.

在此之前,DefaultNodeMatcher已经咨询了ElementSelector来决定如何匹配节点。

Our test has failed because in the default state, XMLUnit will use a depth-first approach to traversing the XMLs and based on document order to match nodes, hence <int> is matched with <boolean>.

我们的测试已经失败了,因为在默认状态下,XMLUnit将使用深度优先的方法来遍历XML,并基于文档顺序来匹配节点,因此<int><boolean>匹配。

Let’s tweak our test so that it passes:

让我们调整一下我们的测试,使其通过。

@Test
public void given2XMLS_whenSimilar_thenCorrect() {
    String controlXml = "<struct><int>3</int><boolean>false</boolean></struct>";
    String testXml = "<struct><boolean>false</boolean><int>3</int></struct>";
    
    assertThat(testXml, 
      isSimilarTo(controlXml).withNodeMatcher(
      new DefaultNodeMatcher(ElementSelectors.byName)));
}

In this case, we are telling DefaultNodeMatcher that when XMLUnit asks for a node to compare, you should have sorted and matched the nodes by their element names already.

在这种情况下,我们要告诉DefaultNodeMatcher,当XMLUnit要求比较一个节点时,你应该已经按元素名称对节点进行了排序和匹配。

The initial failed example was similar to passing ElementSelectors.Default to DefaultNodeMatcher.

最初失败的例子类似于将ElementSelectors.Default传给DefaultNodeMatcher

Alternatively, we could have used a Diff from xmlunit-core rather than using xmlunit-matchers:

另外,我们可以使用来自xmlunit-coreDiff而不是使用xmlunit-matchers

@Test
public void given2XMLs_whenSimilarWithDiff_thenCorrect() 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());
}

6. Custom DifferenceEvaluator

6.自定义DifferenceEvaluator

A DifferenceEvaluator makes determinations of the outcome of a comparison. Its role is restricted to determining the severity of a comparison’s outcome.

一个差异评估器对比较的结果作出判断。它的作用仅限于确定一个比较结果的严重性。

It’s the class that decides whether two XML pieces are identical, similar or different.

它是决定两个XML片段是否相同相似不同的类。

Consider the following XML pieces:

请考虑以下XML片段。

<a>
    <b attr="abc">
    </b>
</a>

and:

和。

<a>
    <b attr="xyz">
    </b>
</a>

In the default state, they are technically evaluated as different because their attr attributes have different values. Let’s take a look at a test:

在默认状态下,它们在技术上被评估为不同,因为它们的attr属性有不同的值。让我们看一下一个测试。

@Test
public void given2XMLsWithDifferences_whenTestsDifferentWithoutDifferenceEvaluator_thenCorrect(){
    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)
      .checkForSimilar().build();
    assertFalse(myDiff.toString(), myDiff.hasDifferences());
}

Failure message:

失败信息。

java.lang.AssertionError: Expected attribute value 'abc' but was 'xyz' - 
  comparing <b attr="abc"...> at /a[1]/b[1]/@attr 
  to <b attr="xyz"...> at /a[1]/b[1]/@attr

If we don’t really care about the attribute, we can change the behavior of DifferenceEvaluator to ignore it. We do this by creating our own:

如果我们并不真正关心这个属性,我们可以改变DifferenceEvaluator的行为以忽略它。我们通过创建我们自己的

public 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;
        final Node controlNode = comparison.getControlDetails().getTarget();
        if (controlNode instanceof Attr) {
            Attr attr = (Attr) controlNode;
            if (attr.getName().equals(attributeName)) {
                return ComparisonResult.SIMILAR;
            }
        }
        return outcome;
    }
}

We then rewrite our initial failed test and supply our own DifferenceEvaluator instance, like so:

然后我们重写我们最初的失败测试,并提供我们自己的DifferenceEvaluator实例,像这样。

@Test
public void given2XMLsWithDifferences_whenTestsSimilarWithDifferenceEvaluator_thenCorrect() {
    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();
    
    assertFalse(myDiff.toString(), myDiff.hasDifferences());
}

This time it passes.

这一次,它通过了。

7. Validation

7.验证

XMLUnit performs XML validation using the Validator class. You create an instance of it using the forLanguage factory method while passing in the schema to use in validation.

XMLUnit使用Validator类来执行XML验证。你使用forLanguage工厂方法创建它的一个实例,同时传入验证中使用的模式。

The schema is passed in as a URI leading to its location, XMLUnit abstracts the schema locations it supports in the Languages class as constants.

模式被作为一个URI传入,导致它的位置,XMLUnit将它在Languages类中支持的模式位置抽象为常数。

We typically create an instance of Validator class like so:

我们通常这样创建一个Validator类的实例。

Validator v = Validator.forLanguage(Languages.W3C_XML_SCHEMA_NS_URI);

After this step, if we have our own XSD file to validate against our XML, we simply specify its source and then call Validator‘s validateInstance method with our XML file source.

在这一步之后,如果我们有自己的XSD文件要针对我们的XML进行验证,我们只需指定其来源,然后用我们的XML文件来源调用ValidatorvalidateInstance方法。

Take for example our students.xsd:

以我们的students.xsd为例。

<?xml version = "1.0"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
    <xs:element name='class'>
        <xs:complexType>
            <xs:sequence>
                <xs:element name='student' type='StudentObject'
                   minOccurs='0' maxOccurs='unbounded' />
            </xs:sequence>
        </xs:complexType>
    </xs:element>
    <xs:complexType name="StudentObject">
        <xs:sequence>
            <xs:element name="name" type="xs:string" />
            <xs:element name="age" type="xs:positiveInteger" />
        </xs:sequence>
        <xs:attribute name='id' type='xs:positiveInteger' />
    </xs:complexType>
</xs:schema>

And students.xml:

还有students.xml

<?xml version = "1.0"?>
<class>
    <student id="393">
        <name>Rajiv</name>
        <age>18</age>
    </student>
    <student id="493">
        <name>Candie</name>
        <age>19</age>
    </student>
</class>

Let’s then run a test:

然后让我们运行一个测试。

@Test
public void givenXml_whenValidatesAgainstXsd_thenCorrect() {
    Validator v = Validator.forLanguage(Languages.W3C_XML_SCHEMA_NS_URI);
    v.setSchemaSource(Input.fromStream(
      XMLUnitTests.class.getResourceAsStream("/students.xsd")).build());
    ValidationResult r = v.validateInstance(Input.fromStream(
      XMLUnitTests.class.getResourceAsStream("/students.xml")).build());
    Iterator<ValidationProblem> probs = r.getProblems().iterator();
    while (probs.hasNext()) {
        probs.next().toString();
    }
    assertTrue(r.isValid());
}

The result of the validation is an instance of ValidationResult which contains a boolean flag indicating whether the document has been validated successfully.

验证的结果是一个ValidationResult的实例,它包含一个布尔标志,表明文档是否被成功验证。

The ValidationResult also contains an Iterable with ValidationProblems in case there is a failure. Let’s create a new XML with errors called students_with_error.xml. Instead of <student>, our starting tags are all </studet>:

ValidationResult还包含一个Iterable,其中有ValidationProblems,以防出现失败。让我们创建一个带有错误的新XML,名为students_with_error.xml。代替<student>,我们的起始标签都是</studet>

<?xml version = "1.0"?>
<class>
    <studet id="393">
        <name>Rajiv</name>
        <age>18</age>
    </student>
    <studet id="493">
        <name>Candie</name>
        <age>19</age>
    </student>
</class>

Then run this test against it:

然后针对它运行这个测试。

@Test
public void givenXmlWithErrors_whenReturnsValidationProblems_thenCorrect() {
    Validator v = Validator.forLanguage(Languages.W3C_XML_SCHEMA_NS_URI);
    v.setSchemaSource(Input.fromStream(
       XMLUnitTests.class.getResourceAsStream("/students.xsd")).build());
    ValidationResult r = v.validateInstance(Input.fromStream(
      XMLUnitTests.class.getResourceAsStream("/students_with_error.xml")).build());
    Iterator<ValidationProblem> probs = r.getProblems().iterator();
    int count = 0;
    while (probs.hasNext()) {
        count++;
        probs.next().toString();
    }
    assertTrue(count > 0);
}

If we were to print the errors in the while loop, they would look like:

如果我们要打印while循环中的错误,它们会看起来像。

ValidationProblem { line=3, column=19, type=ERROR,message='cvc-complex-type.2.4.a: 
  Invalid content was found starting with element 'studet'. 
    One of '{student}' is expected.' }
ValidationProblem { line=6, column=4, type=ERROR, message='The element type "studet" 
  must be terminated by the matching end-tag "</studet>".' }
ValidationProblem { line=6, column=4, type=ERROR, message='The element type "studet" 
  must be terminated by the matching end-tag "</studet>".' }

8. XPath

8.XPath[/strong

When an XPath expression is evaluated against a piece of XML a NodeList is created that contains the matching Nodes.

当一个XPath表达式针对一段XML进行评估时,会创建一个NodeList,其中包含匹配的Nodes

Consider this piece of XML saved in a file called teachers.xml:

考虑一下保存在一个名为teachers.xml的文件中的这段XML。

<teachers>
    <teacher department="science" id='309'>
        <subject>math</subject>
        <subject>physics</subject>
    </teacher>
    <teacher department="arts" id='310'>
        <subject>political education</subject>
        <subject>english</subject>
    </teacher>
</teachers>

XMLUnit offers a number of XPath related assertion methods, as demonstrated below.

XMLUnit提供了许多与XPath相关的断言方法,如下所示。

We can retrieve all the nodes called teacher and perform assertions on them individually:

我们可以检索所有名为teacher的节点,并对它们单独执行断言。

@Test
public void givenXPath_whenAbleToRetrieveNodes_thenCorrect() {
    Iterable<Node> i = new JAXPXPathEngine()
      .selectNodes("//teacher", Input.fromFile(new File("teachers.xml")).build());
    assertNotNull(i);
    int count = 0;
    for (Iterator<Node> it = i.iterator(); it.hasNext();) {
        count++;
        Node node = it.next();
        assertEquals("teacher", node.getNodeName());
        
        NamedNodeMap map = node.getAttributes();
        assertEquals("department", map.item(0).getNodeName());
        assertEquals("id", map.item(1).getNodeName());
        assertEquals("teacher", node.getNodeName());
    }
    assertEquals(2, count);
}

Notice how we validate the number of child nodes, the name of each node and the attributes in each node. Many more options are available after retrieving the Node.

注意我们是如何验证子节点的数量、每个节点的名称和每个节点中的属性的。在检索到Node之后,还有许多选项。

To verify that a path exists, we can do the following:

为了验证路径是否存在,我们可以做以下工作。

@Test
public void givenXmlSource_whenAbleToValidateExistingXPath_thenCorrect() {
    assertThat(Input.fromFile(new File("teachers.xml")), hasXPath("//teachers"));
    assertThat(Input.fromFile(new File("teachers.xml")), hasXPath("//teacher"));
    assertThat(Input.fromFile(new File("teachers.xml")), hasXPath("//subject"));
    assertThat(Input.fromFile(new File("teachers.xml")), hasXPath("//@department"));
}

To verify that a path does not exist, this is what we can do:

为了验证一个路径不存在,我们可以这样做。

@Test
public void givenXmlSource_whenFailsToValidateInExistentXPath_thenCorrect() {
    assertThat(Input.fromFile(new File("teachers.xml")), not(hasXPath("//sujet")));
}

XPaths are especially useful where a document is made up largely of known, unchanging content with only a small amount of changing content created by the system.

XPaths在文件主要由已知的、不变的内容组成,只有少量由系统创建的变化内容的情况下特别有用。

9. Conclusion

9.结论

In this tutorial, we have introduced most of the basic features of XMLUnit 2.x and how to use them to validate XML documents in our applications.

在本教程中,我们介绍了XMLUnit 2.x的大部分基本功能,以及如何使用它们来验证我们应用程序中的XML文档。

The full implementation of all these examples and code snippets can be found in the XMLUnit GitHub project.

所有这些例子和代码片段的完整实现可以在 XMLUnit GitHub项目中找到。