xmlunit:错误 ElementSelectors.conditionalBuilder
xmlunit: Error with ElementSelectors.conditionalBuilder
所以我有以下输入、预期输出和实际输出xml:
input.xml
<Request>
<EmailSubjectLine>Main Contact & 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>
预期-output.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>
实际-output.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)]
我不明白为什么我的元素选择器不起作用,是我使用不当吗?我的目并且无法预测(为了稍后创建更复杂的选择器,例如通过名称和属性比较 ProductRq,因为将名称空间添加到此)。有没有我遗漏的东西,我能否将 2 个 XPath 选择器组合在一起而不是几个 when/then 行和默认大小写?
注意:xml 是通过 xslt 转换的。 PRoductRq 上的命名空间在源文档中不存在;源被复制,命名空间被添加到 ProductRq,然后与一些元素一起被发送输出 removals/modifications/additions
XML单元表示 NotificationRq
中的 RqUID
元素不匹配,当然它们是不同的。
.whenElementIsNamed("NotificationRq")
.thenUse(ElementSelectors.byXPath("./RqUID", ElementSelectors.byName))
意味着:当 XMLUnit 尝试为 NotificationRq
元素寻找伙伴时,它必须搜索具有 RqUID
[=87 的 NotificationRq
=] - 并且只使用 RqUID
元素。
它没有为任何其他元素设置任何规则,特别是 RqUID
本身。对于 RqUID
个元素,默认规则适用并且
.elseUse(ElementSelectors.byNameAndText)
说:XMLUnit 只接受两个成对的元素,前提是它们的名称和嵌套文本匹配。对于有问题的 RqUID
个元素,情况并非如此。
你的整个ElementSelector
说
- 匹配
Audit
s 如果他们有 TransID
children 任意内容。
- 匹配
NotificationRq
个,如果它们有 RqUID
个任意内容。
- 否则使用元素名称和嵌套文本
这不符合您的示例。看看你的 XML 你可能想要
- 通过元素名称和嵌套文本匹配几乎所有内容(尽管从示例中元素名称就足够了)
- 忽略
TransId
children of Audit
s 的嵌套文本
- 忽略
RqUID
children 的 NotificationRq
的嵌套文本
"element named foo
if it is a child of an element named bar
" 没有 built-in 谓词,它可能类似于
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
.
的结构
这本身不足以忽略匹配的 TransId
和 RqUID
元素的嵌套文本,它只会确保 XMLUnit 将选择您想要的节点使用。对于嵌套文本,您需要 DifferenceEvaluator
.
鉴于您默认使用 ElementSelectors.byNameAndText
,您知道嵌套文本对于所有匹配的节点都是相同的,除了您想忽略内容的两个特定元素。所以DifferenceEvaluator
喜欢
DifferenceEvaluators.chain(DifferenceEvaluators.Default,
DifferenceEvaluators.downgradeDifferencesToEqual(ComparisonType.TEXT_VALUE))
应该可以。
所以我有以下输入、预期输出和实际输出xml:
input.xml
<Request>
<EmailSubjectLine>Main Contact & 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>
预期-output.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>
实际-output.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)]
我不明白为什么我的元素选择器不起作用,是我使用不当吗?我的目并且无法预测(为了稍后创建更复杂的选择器,例如通过名称和属性比较 ProductRq,因为将名称空间添加到此)。有没有我遗漏的东西,我能否将 2 个 XPath 选择器组合在一起而不是几个 when/then 行和默认大小写?
注意:xml 是通过 xslt 转换的。 PRoductRq 上的命名空间在源文档中不存在;源被复制,命名空间被添加到 ProductRq,然后与一些元素一起被发送输出 removals/modifications/additions
XML单元表示 NotificationRq
中的 RqUID
元素不匹配,当然它们是不同的。
.whenElementIsNamed("NotificationRq")
.thenUse(ElementSelectors.byXPath("./RqUID", ElementSelectors.byName))
意味着:当 XMLUnit 尝试为 NotificationRq
元素寻找伙伴时,它必须搜索具有 RqUID
[=87 的 NotificationRq
=] - 并且只使用 RqUID
元素。
它没有为任何其他元素设置任何规则,特别是 RqUID
本身。对于 RqUID
个元素,默认规则适用并且
.elseUse(ElementSelectors.byNameAndText)
说:XMLUnit 只接受两个成对的元素,前提是它们的名称和嵌套文本匹配。对于有问题的 RqUID
个元素,情况并非如此。
你的整个ElementSelector
说
- 匹配
Audit
s 如果他们有TransID
children 任意内容。 - 匹配
NotificationRq
个,如果它们有RqUID
个任意内容。 - 否则使用元素名称和嵌套文本
这不符合您的示例。看看你的 XML 你可能想要
- 通过元素名称和嵌套文本匹配几乎所有内容(尽管从示例中元素名称就足够了)
- 忽略
TransId
children ofAudit
s 的嵌套文本
- 忽略
RqUID
children 的NotificationRq
的嵌套文本
"element named foo
if it is a child of an element named bar
" 没有 built-in 谓词,它可能类似于
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
.
这本身不足以忽略匹配的 TransId
和 RqUID
元素的嵌套文本,它只会确保 XMLUnit 将选择您想要的节点使用。对于嵌套文本,您需要 DifferenceEvaluator
.
鉴于您默认使用 ElementSelectors.byNameAndText
,您知道嵌套文本对于所有匹配的节点都是相同的,除了您想忽略内容的两个特定元素。所以DifferenceEvaluator
喜欢
DifferenceEvaluators.chain(DifferenceEvaluators.Default,
DifferenceEvaluators.downgradeDifferencesToEqual(ComparisonType.TEXT_VALUE))
应该可以。