【问题标题】:Compare scala.xml.Elem object in unit test在单元测试中比较 scala.xml.Elem 对象
【发布时间】:2019-06-06 02:17:03
【问题描述】:

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

是否有任何简单的方法来比较两个对象的相等性,忽略 XML 中的属性顺序和无关紧要的空白?

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

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

【问题讨论】:

    标签: xml scala junit


    【解决方案1】:

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

    【讨论】:

    • 我打算发布一个关于修剪空白的后续问题,但您的回答涵盖了这一点。谢谢。
    【解决方案2】:

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

    【讨论】:

    • 我曾考虑过使用 assertTrue。我不知道diff。我会试试的。谢谢。
    【解决方案3】:

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

    【讨论】:

    • 您可能希望使用withClue 来提供上下文,例如包含(即向下)不匹配对的元素的共享路径。
    【解决方案4】:

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

    【讨论】:

      猜你喜欢
      • 2011-01-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-02-26
      • 2020-08-10
      • 2021-12-07
      相关资源
      最近更新 更多