You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何优化ScalaTest参数化测试失败时的对象描述信息?

我之前在ScalaTest里用数组做参数化测试时,也碰到过这种数组显示成[I@xxx哈希值的糟心事,完全看不出数组里实际是什么元素,排查错误特别费劲!咱们来解决这个可读性问题,顺便帮你更快定位数组越界的根源。

核心问题原因

Scala里的数组(比如Array[Int])默认的toString方法就是输出这种类名加哈希码的格式,ScalaTest在报错时直接用了这个默认输出,导致我们看不到数组里的实际元素。另外报错里提到的行号(零基的行2),如果没有测试用例描述,也很难快速对应到具体的测试场景。

解决办法,按易用程度排序:

1. 给断言加上自定义提示(最快见效)

在你的断言语句后面加上withClue,把数组转换成可读的字符串形式。这样一旦断言失败,报错信息里就会显示数组的实际元素了:

sortArray(input) should equal(expected) withClue 
  s" | Input: ${input.mkString("[", ", ", "]")}, Expected: ${expected.mkString("[", ", ", "]")}"

比如原来的报错会变成类似:

ArrayIndexOutOfBoundsException was thrown during property evaluation. (SortingSpec.scala:43)
Message: 2
Occurred at table row 2 (zero based, not counting headings), which had values ( Input = [2, 1], Expected = [1, 2] )
| Input: [2, 1], Expected: [1, 2]

一下子就能看清楚出错的数组内容了。

2. 给测试用例添加描述字段

在你定义的Table里加一个描述列,这样报错时能直接知道是哪个测试场景出了问题,不用对着行号猜:

val testCases = Table(
  ("Test Scenario", "Input", "Expected"),
  ("Odd-length unsorted array", Array(1,3,2), Array(1,2,3)),
  ("Reverse-sorted array", Array(5,4,3,2,1), Array(1,2,3,4,5)),
  ("2-element even array", Array(2,1), Array(1,2)) // 这就是报错里的行2
)

之后报错会显示对应的场景名称,比如:

Occurred at table row 2 (zero based, not counting headings), which had values ( Test Scenario = 2-element even array, Input = [I@17c386de, Expected = [I@5af97850 )

结合上面的withClue,就能完美定位问题了。

3. 用List替代Array(最省心)

如果你的测试逻辑允许,直接把测试数据换成List就行——Scala的List默认toString就是显示元素的(比如List(2,1)),不用做任何额外处理,报错信息里直接就能看到数组内容:

val testCases = Table(
  ("Test Scenario", "Input", "Expected"),
  ("Odd-length unsorted array", List(1,3,2), List(1,2,3)),
  ("Reverse-sorted array", List(5,4,3,2,1), List(1,2,3,4,5)),
  ("2-element even array", List(2,1), List(1,2))
)

4. 自定义数组的toString(适合复杂场景)

如果必须用Array,你可以给数组包装一个简单的类,重写toString方法,或者在定义测试数据时就把数组转换成可读字符串(不过后者会丢失数组类型,需要在测试里再转回来,不太推荐):

// 自定义包装类
case class PrintableArray[T](arr: Array[T]) {
  override def toString: String = arr.mkString("[", ", ", "]")
}

// 测试用例里用包装类
val testCases = Table(
  ("Input", "Expected"),
  (PrintableArray(Array(1,3,2)), PrintableArray(Array(1,2,3))),
  // ...其他用例
)

额外调试技巧

针对你碰到的ArrayIndexOutOfBoundsException,现在能看到数组实际元素后,先检查出错行的输入数组长度,看看你的排序逻辑里是不是有索引操作没处理好边界(比如数组长度是2,代码里却访问了索引2)。比如如果你的排序函数里写了类似arr(i+2)的操作,当数组长度不足时就会触发这个异常。

内容的提问来源于stack exchange,提问作者devoured elysium

火山引擎 最新活动