我正在尝试编写一个应用程序的自动化测试,该应用程序基本上是将自定义消息格式转换为XML消息并将其发送到另一端。我已经有了一组很好的输入/输出消息对,所以我所需要做的就是将输入消息发送进来,并侦听XML消息从另一端传出来。

当需要将实际输出与预期输出进行比较时,我遇到了一些问题。我的第一个想法是对预期消息和实际消息进行字符串比较。这并不能很好地工作,因为我们拥有的示例数据的格式并不总是一致的,而且XML名称空间经常使用不同的别名(有时根本不使用名称空间)。

我知道我可以解析两个字符串,然后遍历每个元素并自己进行比较,这不会太难做到,但我感觉有更好的方法或我可以利用的库。

所以,归结起来,问题是:

给定两个Java字符串,都包含有效的XML,你将如何决定他们是否在语义上等价?如果你有办法确定区别是什么,那就更好了。


当前回答

以Tom的回答为基础,下面是一个使用XMLUnit v2的示例。

它使用这些maven依赖项

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

..这是测试代码

import static org.junit.Assert.assertThat;
import static org.xmlunit.matchers.CompareMatcher.isIdenticalTo;
import org.xmlunit.builder.Input;
import org.xmlunit.input.WhitespaceStrippedSource;

public class SomeTest extends XMLTestCase {
    @Test
    public void test() {
        String result = "<root></root>";
        String expected = "<root>  </root>";

        // ignore whitespace differences
        // https://github.com/xmlunit/user-guide/wiki/Providing-Input-to-XMLUnit#whitespacestrippedsource
        assertThat(result, isIdenticalTo(new WhitespaceStrippedSource(Input.from(expected).build())));

        assertThat(result, isIdenticalTo(Input.from(expected).build())); // will fail due to whitespace differences
    }
}

概括这一点的文档是https://github.com/xmlunit/xmlunit#comparing-two-documents

其他回答

下面将使用标准JDK库检查文档是否相等。

DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
dbf.setCoalescing(true);
dbf.setIgnoringElementContentWhitespace(true);
dbf.setIgnoringComments(true);
DocumentBuilder db = dbf.newDocumentBuilder();

Document doc1 = db.parse(new File("file1.xml"));
doc1.normalizeDocument();

Document doc2 = db.parse(new File("file2.xml"));
doc2.normalizeDocument();

Assert.assertTrue(doc1.isEqualNode(doc2));

Normalize()用于确保没有循环(技术上不会有任何循环)

上面的代码将要求元素中的空格相同,因为它保留并计算它。Java附带的标准XML解析器不允许您设置一个特性来提供规范版本或理解XML:space(如果这将成为一个问题,那么您可能需要替换XML解析器,如xerces或使用JDOM)。

听起来像是XMLUnit的工作

http://www.xmlunit.org/ https://github.com/xmlunit

例子:

public class SomeTest extends XMLTestCase {
  @Test
  public void test() {
    String xml1 = ...
    String xml2 = ...

    XMLUnit.setIgnoreWhitespace(true); // ignore whitespace differences

    // can also compare xml Documents, InputSources, Readers, Diffs
    assertXMLEqual(xml1, xml2);  // assertXMLEquals comes from XMLTestCase
  }
}

既然您说“语义等效”,我假设您的意思是您想做的不仅仅是字面上验证xml输出是(string)等于,并且您想要类似于

<foo>这里有些东西</foo></code>

and

<foo>这里有些东西</foo></code>

阅读时一定要对等。最终,这将关系到您如何定义“语义等效”,无论您从哪个对象重构消息。只需从消息构建该对象,并使用自定义equals()定义您要查找的内容。

以Tom的回答为基础,下面是一个使用XMLUnit v2的示例。

它使用这些maven依赖项

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

..这是测试代码

import static org.junit.Assert.assertThat;
import static org.xmlunit.matchers.CompareMatcher.isIdenticalTo;
import org.xmlunit.builder.Input;
import org.xmlunit.input.WhitespaceStrippedSource;

public class SomeTest extends XMLTestCase {
    @Test
    public void test() {
        String result = "<root></root>";
        String expected = "<root>  </root>";

        // ignore whitespace differences
        // https://github.com/xmlunit/user-guide/wiki/Providing-Input-to-XMLUnit#whitespacestrippedsource
        assertThat(result, isIdenticalTo(new WhitespaceStrippedSource(Input.from(expected).build())));

        assertThat(result, isIdenticalTo(Input.from(expected).build())); // will fail due to whitespace differences
    }
}

概括这一点的文档是https://github.com/xmlunit/xmlunit#comparing-two-documents

使用JExamXML与java应用程序

    import com.a7soft.examxml.ExamXML;
    import com.a7soft.examxml.Options;

       .................

       // Reads two XML files into two strings
       String s1 = readFile("orders1.xml");
       String s2 = readFile("orders.xml");

       // Loads options saved in a property file
       Options.loadOptions("options");

       // Compares two Strings representing XML entities
       System.out.println( ExamXML.compareXMLString( s1, s2 ) );