KotlinTest 中的 shouldBe 和 shouldBe exactly 有什么区别?

What's the difference between shouldBe and shouldBe exactly in KotlinTest?

这是使用 KotlinTest 1.3.5 的测试代码。

val expect = 0.1
val actual: Double = getSomeDoubleValue() 
actual shouldBe expect

并且当代码为 运行 时打印此警告。

[WARN] When comparing doubles consider using tolerance, eg: a shouldBe b plusOrMinus c

在这种情况下,我不想使用 plusOrMinus。 所以,我将代码固定为

val expect = 0.1
val actual: Double = getSomeDoubleValue() 
actual shouldBe exactly(expect)

现在,没有警告。
但是,我想知道 shouldBeshouldBe exactly 之间的区别。这是什么?

比较浮点数,尤其是计算出来的浮点数,总是会出现舍入误差,因为某些浮点数可能无法表示,给出了将它们转换为二进制的方式。因此,在测试时,您通常必须为您认为可接受的舍入误差给出一个值。

在 KotlinTest 中,这是使用 plusOrMinus 说明符指定的。

如果在某些情况下您知道该数字应该恰好是给定值(例如,它被初始化为 0 或 10 或其他),则使用 exactly 说明符。大概这在幕后做了一些事情来指定一个非常小的错误值或以其他方式比较双打。

To assert that a double is exactly equal to another double use d shouldBe exactly(e)
To assert that a double is equal within some tolerance range, use d shouldBe (e plusOrMinus y)

Source

如果您只是单独使用 shouldBe,那么系统只会进行简单的 if a == b 类型测试,这将使您的测试出现上述舍入错误。

根据 current sources:

infix fun Double.shouldBe(other: Double): Unit = ToleranceMatcher(other, 0.0).test(this)

其中 ToleranceMatcher

class ToleranceMatcher(val expected: Double, val tolerance: Double) : Matcher<Double> {

  override fun test(value: Double) {
    if (tolerance == 0.0)
      println("[WARN] When comparing doubles consider using tolerance, eg: a shouldBe b plusOrMinus c")
    val diff = Math.abs(value - expected)
    if (diff > tolerance)
      throw AssertionError("$value is not equal to $expected")
  }

  infix fun plusOrMinus(tolerance: Double): ToleranceMatcher = ToleranceMatcher(expected, tolerance)
}

因此,匹配 d shouldBe e 将精确比较双打,没有任何容差 () 并打印警告:

[WARN] When comparing doubles consider using tolerance, eg: a shouldBe b plusOrMinus c

exactly(d) is defined as

fun exactly(d: Double): Matcher<Double> = object : Matcher<Double> {
    override fun test(value: Double) {
      if (value != d)
        throw AssertionError("$value is not equal to expected value $d")
    }
}

因此 它会做同样的事情,尽管没有任何警告。


我猜,这个警告的意思是鼓励开发人员明确指定双精度进行精确比较,或者指定容差,因为即使以不同的顺序完成相同的算术也可能会产生与双精度不同的结果。