Compare scala.xml.Elem object in unit test

前端 未结 4 1882
执笔经年
执笔经年 2021-02-19 06:21

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 objec

相关标签:
4条回答
  • 2021-02-19 06:53

    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...
    
    0 讨论(0)
  • 2021-02-19 06:54

    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>)
    
    0 讨论(0)
  • 2021-02-19 06:54

    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))
    
    }
    
    0 讨论(0)
  • 2021-02-19 07:00

    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)
    }
    
    0 讨论(0)
提交回复
热议问题