我有两个scala.xml.Elem
个对象(实际的,预期的)。我使用的是JUnit 4,但也包含了XMLUnit 1.3。
有没有简单的方法来比较两个对象的相等性,忽略XML中的属性顺序和无关紧要的空格?
我尝试XMLUnit.assertXMLEqual()
,但它抱怨类型为scala.xml.Elem
。
我知道我可以使用equals
或==
,但我希望断言在不相等时打印两个值。如果我使用assertTrue(actual.equals(expected))
,并且它们不相等,则唯一的输出将是“断言失败”。
答案 0 :(得分:17)
如果要与忽略空格的XML Elem
对象进行比较,可以使用scala.xml.Utility.trim
方法从中删除空格。
scala> val a = <foo>bar</foo>
a: scala.xml.Elem = <foo>bar</foo>
scala> val b = <foo> bar </foo>
b: scala.xml.Elem = <foo> bar </foo>
scala> a == b
res8: Boolean = false
scala> import scala.xml.Utility.trim
import scala.xml.Utility.trim
scala> trim(a) == trim(b)
res9: Boolean = true
如果使用XML文字,Scala不关心属性的顺序:
scala> val a = <foo first="1" second="2" />
a: scala.xml.Elem = <foo first="1" second="2"></foo>
scala> val b = <foo second="1" first="1" />
b: scala.xml.Elem = <foo first="1" second="1"></foo>
scala> a == b
res22: Boolean = true
我建议ScalaTest进行单元测试,你有ShouldMatchers
:
// Scala repl started with scalatest-1.2.jar in the classpath
scala> val a = <foo>bar</foo>
a: scala.xml.Elem = <foo>bar</foo>
scala> val b = <foo>bar</foo>
b: scala.xml.Elem = <foo>bar</foo>
scala> a should equal(b)
scala> val b = <foo>bar2</foo>
b: scala.xml.Elem = <foo>bar2</foo>
scala> a should equal(b)
org.scalatest.TestFailedException: <foo>bar</foo> did not equal <foo>bar2</foo>
at org.scalatest.matchers.Matchers$class.newTestFailedException(Matchers.scala:148)
at org.scalatest.matchers.ShouldMatchers$.newTestFailedException(ShouldMatchers.scala:2329)
at org.scalatest.matchers.ShouldMatchers$ShouldMethodHelper$.shouldMatcher(ShouldMatchers.scala:871)
at org.scalatest.matchers.ShouldMatchers$SeqShouldWrapper.should(ShouldMatchers.scala:1724)
at .<init>(<console>:15)
at .<clinit>(<console>)
at RequestResult$.<init>(<console>:9)
at RequestResult$.<clinit>(<console>)
at RequestResult$scala_repl_result(<console>)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.Delega...
答案 1 :(得分:10)
使用允许传递自定义消息的assertTrue
版本
public static void assertTrue(java.lang.String message,
boolean condition)
和(例如)diff
生成具有不等于的下降节点的字符串
scala> val xml1 = <person><name>john</name><lastname>smith</lastname></person>
xml1: scala.xml.Elem = <person><name>john</name><lastname>smith</lastname></person>
scala> val xml2 = <person><name>jane</name><lastname>smith</lastname></person>
xml2: scala.xml.Elem = <person><name>jane</name><lastname>smith</lastname></person>
scala> assert(xml1 == xml2, xml1.child diff xml2.child mkString(", "))
java.lang.AssertionError: assertion failed: <name>john</name>
at scala.Predef$.assert(Predef.scala:91)
at .<init>(<console>:8)
at .<clinit>(<console>)
答案 2 :(得分:1)
早期的答案对我有帮助,尽管我发现有时候我想检查更大的XML块,并且显示两块XML的故障比较有点难以理解。此方法将首先尝试递归到子元素以比较这些元素,因此如果深层嵌套元素不正确,它将显示更简洁的错误。根据您的XML,这可能无法为您提供足够的上下文来确定其实际失败的位置,但我发现它很有用。
/** Check that the XMLs are the same, ignoring empty text nodes (whitespace). */
private def assertEqual(actual: xml.Node, expected: xml.Node) {
def recurse(actual: xml.Node, expected: xml.Node) {
// depth-first checks, to get specific failures
for ((actualChild, expectedChild) <- actual.child zip expected.child) {
recurse(actualChild, expectedChild)
}
actual should be (expected)
}
recurse(scala.xml.Utility.trim(actual), scala.xml.Utility.trim(expected))
}
答案 3 :(得分:0)
我修改了@Nick的代码以与JDom2一起使用。在他的代码中,由于zip
的工作原理,如果expectedXML
的尾随元素不在actualXML
中,则测试通过。我修复了该错误,并使尾随元素的比较为可选:
trait XMLTest extends XMLSupport {
/** Verify that the XMLs are the same, regardless of attribute or element ordering and ignoring whitespace. */
def assertEqual(actual: Element, expected: Element, ignoreTrailingElements: Boolean=false): Assertion = {
// depth-first comparison
def recurse(actual: Element, expected: Element): Assertion = {
import scala.collection.JavaConverters._
val actualChildren: Seq[Element] = actual.getChildren.asScala.sortBy(_.getName)
val expectedChildren: Seq[Element] = expected.getChildren.asScala.sortBy(_.getName)
(actualChildren zip expectedChildren) foreach { case (actualChild, expectedChild) =>
recurse(actualChild, expectedChild)
}
actual.getName shouldEqual expected.getName
actual.getTextNormalize shouldEqual expected.getTextNormalize
actual.getAttributes.asScala.map(_.toString).sorted shouldEqual expected.getAttributes.asScala.map(_.toString).sorted
if (!ignoreTrailingElements && actualChildren.size < expectedChildren.size) {
val diff = expectedChildren.drop(actualChildren.size)
fail("Extra XML children found: " + prettyPrint(diff))
} else succeed
}
recurse(actual, expected)
}
}
我写了这个特征以混合到测试代码中:
trait XMLSupport {
import org.jdom2.output.{Format, XMLOutputter}
def prettyPrint(doc: Document): String = {
val xmlOutput = new XMLOutputter()
xmlOutput.setFormat(Format.getPrettyFormat)
xmlOutput.outputString(doc)
}
def prettyPrint(elements: Seq[Element]): String = {
import scala.collection.JavaConverters._
val xmlOutput = new XMLOutputter()
xmlOutput.setFormat(Format.getPrettyFormat)
xmlOutput.outputString(elements.asJava)
}
}
我以这种方式调用测试:
class XmlTest extends WordSpec with MustMatchers {
// test code here
assertEqual(actualXML.getRootElement, expectedXML.getRootElement, ignoreTrailingElements=true)
}