比较列表时,“应该是”和“===”之间的Scala FunSuite区别

时间:2015-10-28 16:43:54

标签: java scala unit-testing scalatest

我有一个关于将FunSuite与Matchers一起使用的问题。

我正在尝试与对象列表进行比较,所以我尝试这样做:

orders2 should be(tiOrdersList2)

其中orders2和tiOrdersList2是scala.List [MyObject]

这场比赛失败了,所以我尝试了

orders2.toSeq should be (tiOrdersList2.toSeq)

没有运气

然后我尝试匹配单个项目

orders2(0) should be (tiOrdersList2(0))

再没有运气

最后我发现为了使它能够用于单个对象我必须使用“===”并回到第一个案例我设法让这个工作:

orders2 === tiOrdersList2

我无法理解这里有什么区别。 我认为“应该是”匹配器比“===”更严格,但我不明白为什么。 任何人都可以向我解释一下吗?

注意:我想指出MyObject具有不同基于java的类型的各种属性,包括float。可能是这个原因?

更新 我用简单的类检查了matcher(参见下面的代码片段),如果我自己覆盖equals方法,那么“should be”也可以,所以主要的问题是“===”做什么样的比较?

import org.scalatest.{Matchers, FunSuite}
import org.junit.runner.RunWith
import org.scalatest.junit.JUnitRunner

@RunWith(classOf[JUnitRunner])
class blahSuite extends FunSuite  with Matchers
{
    test("extractSessions") {
        new Object1 === (new Object1)
        new Object2 === (new Object2)
        new Object1 shouldEqual  (new Object1)
        new Object2 should be (new Object2)
    }

    class Object1 {
        val pippo: java.lang.String = "blah"
        val pappo:java.lang.Long = 0l
        override def toString(): String = {
            s"$pippo, $pappo"
        }
    }
    class Object2 {
        val pippo: java.lang.String = "blah"
        val pappo:java.lang.Long = 0l
        val pluto:java.lang.Float = 0.0f
        override def toString(): String = {
            s"$pippo, $pappo, $pluto"
        }
    }
}

UPDATE2 似乎“===”非常宽松地比较:我尝试了其他实例,其中一个对象的内容是不同的(即不同的“pippo”字符串),它告诉我它们是相同的。 所以它似乎比较了类的类型,而不是用反射做一些魔术...... 我对此越来越感到困惑,并开始认为我应该实现我的equals方法而忘记这个......

1 个答案:

答案 0 :(得分:2)

关于===

我认为===并不完全符合您的想法。您应该使用assert(1 === 4)。正如scalatest doc中指出的那样:

  

ScalaTest允许您使用Scala的断言语法,但定义了三元组   equals运算符(===)为您提供更好的错误消息。下列   代码会给你一个错误,只显示一个断言失败:

assert(1 == 2) 
     

使用三等于代替会给你更多   信息性错误消息," 1不等于2":

assert(1 === 2)

因此=====相同。

(请注意,docs中提到的匹配器===中也可以result should === (3),但我不认为这对您来说非常重要,只是为了让你知道他们是不同的野兽)

应该(工作?)

Should be也不起作用。问题是第一个失败的断言(shouldEqual)抛出错误并且执行停止,所以我们从未运行过should be。如果您更改了订单,您还会看到should be的错误:

new Object2 should be (new Object2)
new Object1 shouldEqual  (new Object1)

[info] - extractSessions *** FAILED ***
[info]   blah, 0, 0.0 was not equal to blah, 0, 0.0 (testa.scala:22)

<强>比较

正如您已经发现的那样,我们无法比较对象:

val a = new Object1
val b = new Object1

println(a == b) // false

// Or the equivalent version

println(a.equals(b)) // false

粗略地说,当你打电话给==时,你实际上是在打电话给你:

println(a.hashCode == b.hashCode) // false

由于hashCode的默认实现基于当前对象的内存地址,因此它们显然不相同:

println(a.hashCode)  // 769724695
println(b.hashCode)  // 757278160

所以是的,你必须......

重新实现等于和hashCode

就像在Scala Cookbook recipe中所做的那样,你可以这样做:

test("extractSessions") {
  new Object3 should be (new Object3)
  new Object3 shouldEqual (new Object3)
}

class Object3 {
  // PS: A little code improvement: 
  // I removed the old ugly, java like
  // val pippo: java.lang.String = "blah" 
  // with the more scala-like:  
  val pippo = "blah"  // Scala guesses the type -> String
  val pappo = 0L      // again -> Long

  def canEqual(a: Any) = a.isInstanceOf[Object3]

  override def equals(that: Any): Boolean =
    that match {
      case that: Object3 => that.canEqual(this) && this.hashCode == that.hashCode
      case _             => false
  }

  override def hashCode: Int = (41 * (41 + pippo.hashCode) + pappo.hashCode)
}

所以......我总是需要为我的对象重新实现hashCode和equals。这是这样吗?这都是?

没有!

Scala可以为您做更多的事情!

案例类

在类定义之前使用关键字case时,您需要创建case class。这个以及其他不错的功能会自动为您实现hashcodeequalstoString方法。

现在,我们可以有类似的东西:

// ...

test("extractSessions") {
  assert(Object1("blah", 0L) === Object1("blah", 0L))
  assert(Object2("blah", 0L, 0.0F) === Object2("blah", 0L, 0.0F))

  Object1("blah", 0L) shouldEqual (Object1("blah", 0L))
  Object2("blah", 0L, 0.0F) should be (Object2("blah", 0L, 0.0F))

  // Also works
  new Object1("blah", 0L) shouldEqual (new Object1("blah", 0L))
}

case class Object1(pippo: String, pappo: Long)

case class Object2(pippo: String, pappo: Long, pluto: Float)

// ...    

如果你真的想深入了解并希望更深入地了解平等解决方案和陷阱,我建议马丁的Odersky的文章How to Write an Equality Method in Java来理解Java中的一些平等问题。这有助于理解为什么有些东西就像它们在Scala中一样。