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)。
解决方法:
分别验证案例class的每个"part"是否相等:
prod.get.prices should be(expectedProd.get.prices)
prod.get.id should be(expectedProd.get.id)
如果使用 Array
不是必须的,您可以将大小写 class 更改为使用 Seq[Int]
,这将使测试通过,因为 Seq
对equals
的实现是"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个值是否属于同一类型,但是由于类型擦除,类型参数没有被检查,所以在某些情况下,类型参数值可能被确定为相等,即使它们不是同一类型。
我正在尝试为 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)。
解决方法:
分别验证案例class的每个"part"是否相等:
prod.get.prices should be(expectedProd.get.prices) prod.get.id should be(expectedProd.get.id)
如果使用
Array
不是必须的,您可以将大小写 class 更改为使用Seq[Int]
,这将使测试通过,因为Seq
对equals
的实现是"deep"
比较数组:
当比较 "on their own" 时,匹配器的 should be
:
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个值是否属于同一类型,但是由于类型擦除,类型参数没有被检查,所以在某些情况下,类型参数值可能被确定为相等,即使它们不是同一类型。