问题描述
我在使用 ScalaCheck 学习基于属性的 Scala 测试时,偶然发现了有关 stackoverflow 的这一挑战。
Find the smallest positive integer that does not occur in a given sequence
我想尝试为此问题编写一个基于生成器驱动的属性的测试来检查我的程序的有效性,但似乎无法想到如何编写相关的测试用例。我知道我可以为这个用例编写一个基于表驱动的属性的测试,但这限制了我可以用来测试这个算法的属性数量。
import scala.annotation.tailrec
object Solution extends App {
def solution(a: Array[Int]): Int = {
val posNums = a.toSet.filter(_ > 0)
@tailrec
def checkForSmallestNum(ls: Set[Int],nextMin: Int): Int = {
if (ls.contains(nextMin)) checkForSmallestNum(ls,nextMin + 1)
else nextMin
}
checkForSmallestNum(posNums,1)
}
}
解决方法
使用 Scalatest 的(因为您标记了 scalatest
)Scalacheck 集成和 Scalatest 匹配器,例如
forAll(Gen.listOf(Gen.posNum[Int]) -> "ints") { ints =>
val asSet = ints.toSet
val smallestNI = Solution.solution(ints.toArray)
asSet shouldNot contain(smallestNI)
// verify that adding non-positive ints doesn't change the result
forAll(
Gen.frequency(
1 -> Gen.const(0),10 -> Gen.negNum[Int]
) -> "nonPos"
) { nonPos =>
// Adding a non-positive integer to the input shouldn't affect the result
Solution.solution((nonPos :: ints).toArray) shouldBe smallestNI
}
// More of a property-based approach
if (smallestNI > 1) {
forAll(Gen.oneOf(1 until smallestNI) -> "x") { x =>
asSet should contain(x)
}
} else succeed // vacuous
// Alternatively,but perhaps in a less property-based way
(1 until smallestNI).foreach { x =>
asSet should contain(x)
}
}
请注意,如果将 scalatest 设置为尝试 forAll
s 100 次,则嵌套属性检查将检查值 10k 次。由于 smallestNI
几乎总是小于试验次数(因为 listOf
很少生成长列表),因此详尽检查实际上比嵌套属性检查更快。
总体技巧是,如果某项是某个谓词适用的最小正整数,则等于说对于所有小于该谓词不适用的正整数的所有正整数。