ScalaTest深度比较最佳实践

ScalaTest deep comparison best practices

我正在尝试为 returns 包含数组的 class 元组的函数编写单元测试。

一个简单的 assert(out === expectedOut)out should be(expectedOut) 不比较 class 的内容,因为数组的缘故。在 ScalaTest 中有没有一种巧妙的方法来做到这一点?

我查看了自定义匹配器,但我不确定这是否是我尝试做的事情的最佳方式。因此,我们将不胜感激来自专家经验的任何信息。

编辑: 这是一个似乎并非如此的案例:

object Utils {
  case class Product(id: Int, prices: Array[Int])


  def getProductInfo(id: Int, prices: Array[Int]): Option[Product] = {
    val sortedPrices = prices.sortWith(_ < _)
    Some(Product(id, sortedPrices))
  }
}

---

import org.scalatest._
import Utils._

class DataProcessorSpec extends FlatSpec with Matchers with OptionValues {
  val id = 12345
  val priceList = Array(10,20,30)

  val prod = Utils.getProductInfo(id, priceList)
  val expectedProd = Some(Utils.Product(id, priceList))

  "A DataProcessorSpec" should "return the correct product information" in {
     prod should be(expectedProd)
  }
}

测试失败,因为 sortWith 导致创建一个新数组,因此指向不同的内存位置,据我所知。

UPDATE: 有了代码示例,就更清楚了:

比较失败,因为shoud be使用caseclass的equals函数来进行比较,而caseclasses不比较数组"deeply" - 这意味着,正如您所怀疑的那样,不同的实例将不相等(查看更多信息 here)。

解决方法

  1. 分别验证案例class的每个"part"是否相等:

    prod.get.prices should be(expectedProd.get.prices)
    prod.get.id should be(expectedProd.get.id)
    
  2. 如果使用 Array 不是必须的,您可以将大小写 class 更改为使用 Seq[Int],这将使测试通过,因为 Seqequals的实现是"deep"

比较数组:

当比较 "on their own" 时,匹配器的 should be:

按预期 ("deeply") 比较了数组
arr1 should be(arr2) // true if contents is the same

如果你只是想比较内容,而不去验证out确实是一个Array,你可以使用theSameElementsInOrderAs:

arr1 should contain theSameElementsInOrderAs arr2

正如 Tzach Zohar 在他的回答中所说,should be 使用案例 class 的 equals 不“深入”地比较数组。

您可以创建一个函数来深入比较任何 2 个案例 classes(以及任何其他 Product 合法继承人):

def deepEquals(x: Product, y: Product): Unit = {
  if (x.getClass == y.getClass) {
    x.productIterator.zip(y.productIterator)
      .foreach {
        case (xElem: Product, yElem: Product) => deepEquals(xElem, yElem)
        case (xElem, yElem) => xElem should be(yElem)
      }
  } else {
    x should be(y) // Will fail, with an appropriate error
  }
}

注意:这个函数使用.getClass来检查2个值是否属于同一类型,但是由于类型擦除,类型参数没有被检查,所以在某些情况下,类型参数值可能被确定为相等,即使它们不是同一类型。