所以我有以下输入,预期输出和实际输出xml:

input.xml

<Request>
  <EmailSubjectLine>Main Contact &amp; No Reported To</EmailSubjectLine>
  <ProductRq>
      <Signon>
        <ClientDt>1/6/2017 11:25:45 AM</ClientDt>
        <CustLangPref>en-US</CustLangPref>
      </Signon>
      <SvcRq>
          <RqUID>xxxxxxxx-2802-xxxx-xxxx-bf8361xxxxxx</RqUID>
          <NotificationRq>
              <TransactionRequestDt>2017-01-06</TransactionRequestDt>
              <Currency>USD</Currency>
          </NotificationRq>
      </SvcRq>
  </ProductRq>
<!-- rest of input -->
</Request>


预期输出.xml

<ProductRq xmlns="http://test.org/standards/intake">
    <Audit>
        <TransID>Test</TransID>
    </Audit>
    <Signon>
        <ClientDt>1/6/2017 11:25:45 AM</ClientDt>
        <CustLangPref>en-US</CustLangPref>
    </Signon>
    <SvcRq>
        <RqUID>xxxxxxxx-2802-xxxx-xxxx-bf8361xxxxxx</RqUID>
        <NotificationRq>
            <RqUID>Test</RqUID>
            <TransactionRequestDt>2017-01-06</TransactionRequestDt>
            <Currency>USD</Currency>
        </NotificationRq>
    </SvcRq>
    <!-- rest of expected-output -->
</ProductRq>


实际输出.xml

<ProductRq xmlns="http://test.org/standards/intake">
    <Audit>
        <TransID>123534Abwe-asdcv-1258qw-asd</TransID>
    </Audit>
    <Signon>
        <ClientDt>1/6/2017 11:25:45 AM</ClientDt>
        <CustLangPref>en-US</CustLangPref>
    </Signon>
    <SvcRq>
        <RqUID>xxxxxxxx-2802-xxxx-xxxx-bf8361xxxxxx</RqUID>
        <NotificationRq>
            <RqUID>CG-17Dawe-12354-Hw35Sf</RqUID>
            <TransactionRequestDt>2017-01-06</TransactionRequestDt>
            <Currency>USD</Currency>
        </NotificationRq>
    </SvcRq>
    <!-- rest of actual-output -->
</ProductRq>


我将它们与以下Diff设置进行比较:

MyTest.java

        Diff diff = DiffBuilder
                .compare(xmlExpectedOutput)
                .withTest(xmlOutput)
                .normalizeWhitespace()
                .withNodeMatcher(new DefaultNodeMatcher(ElementSelectors.conditionalBuilder()
                        .whenElementIsNamed("Audit")
                        .thenUse(ElementSelectors.byXPath("./TransID", ElementSelectors.byName))
                        .whenElementIsNamed("NotificationRq")
                        .thenUse(ElementSelectors.byXPath("./RqUID", ElementSelectors.byName))
                        .elseUse(ElementSelectors.byNameAndText)
                        .build()
                        ))
                .checkForSimilar()
                .build();


当我运行上述输入并将其与Expected-output.xml进行比较时,我得到以下区别:

[Expected child '{http://test.org/standards/intake}RqUID' but was 'null' - comparing <RqUID...> at /ProductRq[1]/SvcRq[1]/NotificationRq[1]/RqUID[1] to <NULL> (DIFFERENT), Expected child 'null' but was '{http://test.org/standards/intake}RqUID' - comparing <NULL> to <RqUID...> at /ProductRq[1]/SvcRq[1]/NotificationRq[1]/RqUID[1] (DIFFERENT)]

我不明白为什么我的Element选择器不起作用,我使用不正确吗?我的目标是在找到TransmissionId或NotificationRq / RqUID时,仅通过名称将它们与预期的输出版本匹配,否则将名称和文本用于其他元素,因为这些元素包含唯一的生成的ID,这些ID会更改每次测试运行并且无法预测(以期以后创建一个更复杂的选择器,例如,通过名称和属性比较ProductRq来添加名称空间)。我缺少什么吗,我是否可以将两个XPath选择器组合在一起,而不是将多个when / then行和默认情况组合在一起?

注意:xml是通过xslt转换的。源文件上没有PRoductRq上的名称空间;复制源,将名称空间添加到ProductRq,然后与一些元素删除/修改/添加一起发送以进行输出

最佳答案

XMLUnit表示RqUID中的NotificationRq元素不匹配,当然它们是不同的。

.whenElementIsNamed("NotificationRq")
.thenUse(ElementSelectors.byXPath("./RqUID", ElementSelectors.byName))


意思是:当XMLUnit尝试为NotificationRq元素寻找伙伴时,它必须搜索具有NotificationRq子元素的RqUID-仅使用RqUID元素。

它没有为任何其他元素设置任何规则,尤其是RqUID本身。对于RqUID元素,默认规则适用,

.elseUse(ElementSelectors.byNameAndText)


说:XMLUnit只接受两个元素作为对,如果它们的名称和嵌套文本匹配。所讨论的RqUID元素不是这种情况。

您整个ElementSelector


如果Audit的子元素具有任意内容,则匹配TransID
如果NotificationRq具有任意内容,则匹配RqUID
否则使用元素名称和嵌套文本


这不适合您的示例。查看您可能想要的XML


通过元素名称和嵌套文本匹配几乎所有内容(尽管从示例中,元素名称就足够了)
忽略TransId个子元素的Audit个嵌套文本
忽略RqUIDNotificationRq子级的嵌套文本


对于“名为foo的元素,如果它是名为bar的元素的子元素”,则没有内置谓词,它可能类似于

Predicate<Element> transIdInAudit = e -> {
     if (e == null || e.getParentNode() == null) {
         return false;
     }
     return "TransID".equals(e.getLocalName()) && "Audit".equals(e.getParentNode().getLocalName());
};


您可能想使它泛化:-)

这样你会用

.withNodeMatcher(new DefaultNodeMatcher(ElementSelectors.conditionalBuilder()
    .when(transIdInAudit)
    .thenUse(ElementSelectors.byName)
    .when(rqUIDInNotificationRq) // similar to transIdInAudit
    .thenUse(ElementSelectors.byName)
    .elseUse(ElementSelectors.byNameAndText)
    .build())


如果它们具有匹配的SvcRq,也许您真的想匹配RqUID,也许没有。如果是这样,您将使用当前用于NotificationRq的结构。

这本身不足以忽略匹配的TransIdRqUID元素的嵌套文本,只能确保XMLUnit选择您要使用的节点。对于嵌套文本,您将需要DifferenceEvaluator

假设默认情况下使用的是ElementSelectors.byNameAndText,则您知道所有匹配节点的嵌套文本都相同,除了您要忽略内容的两个特定元素之外。所以像DifferenceEvaluator

DifferenceEvaluators.chain(DifferenceEvaluators.Default,
    DifferenceEvaluators.downgradeDifferencesToEqual(ComparisonType.TEXT_VALUE))


应该管用。

10-06 10:19