这是一个使用KotlinTest 1.3.5的测试代码。
val expect = 0.1
val actual: Double = getSomeDoubleValue()
actual shouldBe expect
,并且在运行代码时打印此警告。
[WARN]当比较双精度时考虑使用公差,例如:a应该是b +或- c
在本例中,我不想使用plusOrMinus
。因此,我将代码固定为
val expect = 0.1
val actual: Double = getSomeDoubleValue()
actual shouldBe exactly(expect)
现在,没有警告。
但是,我想知道shouldBe
和shouldBe exactly
的区别。这是什么?
根据当前来源:
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
将精确地比较双精度值,没有任何偏差(a - b
永远不会为不同的双精度值提供0
),并打印警告:
[WARN]当比较双精度时考虑使用公差,例如:a应该是b +或- c
而exactly(d)
定义为
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")
}
}
所以它会做同样的,但没有任何警告。
我猜,这个警告的含义是鼓励开发人员显式地指定精确比较双精度,或者指定容差,因为即使以不同顺序执行相同的算术运算,也可能对双精度产生不同的结果。
比较浮点数,特别是经过计算的浮点数,总是会产生舍入误差,因为某些浮点数在转换为二进制时可能无法表示。因此,在测试时,您通常必须为您认为可接受的舍入误差提供一个值。
在KotlinTest中,这是使用plusOrMinus
说明符指定的。
如果在某些情况下,你知道数字应该是一个给定的值(例如,它被初始化为0或10或其他),那么使用exactly
说明符。据推测,这在幕后做了一些事情来指定一个非常小的错误值,或者以其他方式比较双精度。
来源要断言一个双精度对象与另一个双精度对象完全相等,使用d应该是exactly(e)
要断言双精度对象在某个容差范围内相等,使用d shouldBe (e plus or - y)
如果你只是自己使用shouldBe
,那么系统将只做一个简单的if a == b
类型测试,这将使你的测试暴露于上述舍入误差