在单元测试中比较 scala.xml.Elem 对象

发布于 2024-10-04 05:45:57 字数 328 浏览 1 评论 0原文

我有两个 scala.xml.Elem 对象(实际的、预期的)。我使用的是 JUnit 4,但也包含了 XMLUnit 1.3。

有没有简单的方法可以比较两个对象的相等性,忽略 XML 中的属性顺序和无关紧要的空格?

我尝试了 XMLUnit.assertXMLEqual(),但它抱怨类型是 scala.xml.Elem。

我知道我可以使用 equals==,但我希望让断言在两个值不相等时打印它们的好处。如果我使用assertTrue(actual.equals(expected)),并且它们不相等,则唯一的输出将是“断言失败”。

I have two scala.xml.Elem objects (actual, expected). I am using JUnit 4, but have also included XMLUnit 1.3.

Is there any easy way to compare the two objects for equality, ignoring attribute order and insignificant whitespace in the XML?

I tried XMLUnit.assertXMLEqual(), but it complains that the types are scala.xml.Elem.

I know that I can use equals or ==, but I would like the benefit of having the assertion print the two values when they are not equal. If I use assertTrue(actual.equals(expected)), and they are not equal, the only output will be "assertion failed".

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

冬天的雪花 2024-10-11 05:45:57

如果您想与忽略空格的 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...

If you want to compare to XML Elem objects ignoring whitespaces you can remove the whitespaces from them with scala.xml.Utility.trim method.

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

Scala does not care about the order of the attributes if you use XML literals:

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

I would recommend ScalaTest for unit testing there you have the 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...
傻比既视感 2024-10-11 05:45:57

使用允许传递自定义消息的 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>)

Use the version of assertTrue that allows passing custom messages

public static void assertTrue(java.lang.String message,
                              boolean condition)

and (for example) diff to produce the string with the descendand nodes that aren't equal

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>)
始于初秋 2024-10-11 05:45:57

早期的答案对我很有帮助,尽管我发现有时我想检查较大的 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))

}

The earlier answers were helpful to me, though I found that sometimes I wanted to check a larger chunk of XML and the failure comparison showing both chunks of XML was a bit hard to read. This method will try to recurse down into child elements first to compare those, so if a deeply nested element is incorrect it will show a much more concise error. Depending on your XML this might not give you enough context to work out where it's actually failing, but I find it useful.

/** 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))

}
生生漫 2024-10-11 05:45:57

我修改了 @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)
}

I modified @Nick's code to work with JDom2. In his code, because of how zip works, if expectedXML has trailing elements that are not in actualXML, the test passes. I fixed that bug, and made the comparison of trailing elements optional:

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)
  }
}

I wrote this trait to mix into the test code:

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)
  }
}

I invoked the test this way:

class XmlTest extends WordSpec with MustMatchers {
  // test code here
  assertEqual(actualXML.getRootElement, expectedXML.getRootElement, ignoreTrailingElements=true)
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文