我正在尝试用基于属性的测试(PBT)替换一些旧的单元测试,具体地用scala
andscalatest - scalacheck
但我认为问题更普遍。简化的情况是,如果我有要测试的方法:
def upcaseReverse(s:String) = s.toUpperCase.reverse
通常,我会编写单元测试,例如:
assertEquals("GNIRTS", upcaseReverse("string"))
assertEquals("", upcaseReverse(""))
// ... corner cases I could think of
所以,对于每个测试,我都会写出我期望的输出,没问题。现在,使用 PBT,它会是这样的:
property("strings are reversed and upper-cased") {
forAll { (s: String) =>
assert ( upcaseReverse(s) == ???) //this is the problem right here!
}
}
当我尝试编写一个适用于所有String
输入的测试时,我发现自己不得不在测试中再次编写该方法的逻辑。在这种情况下,测试看起来像:
assert ( upcaseReverse(s) == s.toUpperCase.reverse)
也就是说,我必须在测试中编写实现以确保输出正确。有没有办法解决这个问题?我是否误解了 PBT,我是否应该测试其他属性,例如:
- “字符串的长度应与原始字符串相同”
- “字符串应该包含原始的所有字符”
- “字符串不应包含小写字符” ...
这也是合理的,但听起来很做作,不太清楚。任何在 PBT 方面有更多经验的人都可以在这里阐明一下吗?
编辑:按照@Eric的资料,我得到了这篇文章,并且有一个我的意思的例子(在再次应用类别时):测试 times
(F#
)中的方法:
type Dollar(amount:int) =
member val Amount = amount
member this.Add add =
Dollar (amount + add)
member this.Times multiplier =
Dollar (amount * multiplier)
static member Create amount =
Dollar amount
作者最终编写了一个测试,如下所示:
let ``create then times should be same as times then create`` start multiplier =
let d0 = Dollar.Create start
let d1 = d0.Times(multiplier)
let d2 = Dollar.Create (start * multiplier) // This ones duplicates the code of Times!
d1 = d2
因此,为了测试该方法,该方法的代码在测试中被复制。在这种情况下,像乘法一样微不足道,但我认为它可以外推到更复杂的情况。