【发布时间】:2015-09-05 10:54:17
【问题描述】:
我正在尝试用基于属性的测试 (PBT) 替换一些旧的单元测试,用 scala 和 scalatest - 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 的消息来源,我找到了this post,这正是我的意思的一个例子(在再次应用类别):测试方法times in (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
因此,为了测试一个方法,该方法的代码在测试中被复制。在这种情况下,像乘法一样微不足道,但我认为它可以外推到更复杂的情况。
【问题讨论】:
-
@Eric,根据您提到的演示文稿的来源,我添加了更多信息
标签: unit-testing scalatest scalacheck property-based-testing