基于属性的测试
ScalaCheck
基于属性的测试 是 CoddyKit 上的免费 Scala for Backend Engineering & Functional Programming 课时。 这是第 3 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Scala for Backend Engineering & Functional Programming 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Scala for Backend Engineering & Functional Programming 课程共包含 4 节课。
什么是基于属性的测试
在基于属性的测试中,您声明一个对所有输入都应成立的一般属性,然后由框架生成大量随机输入,尝试证明该属性不成立。这可以发现手写示例遗漏的边界情况。
示例与属性
示例测试检查一个案例:reverse(List(1,2,3)) == List(3,2,1)。属性则描述一条规则:对任意列表反转两次都会得到原列表。框架会在数百个生成的列表上检查这条规则。
ScalaCheck
ScalaCheck 是 Scala 的属性测试库。将它添加为测试依赖后,它可以与 ScalaTest 集成。
libraryDependencies += "org.scalatestplus" %% "scalacheck-1-17" % "3.2.18.0" % TestforAll
核心构造是 forAll:它会生成随机值,并对每个值断言属性主体。在这里,我们测试加法是否满足交换律。
import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers
import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks
class AddSpec extends AnyFlatSpec with Matchers with ScalaCheckPropertyChecks {
"addition" should "be commutative" in {
forAll { (a: Int, b: Int) =>
a + b shouldBe b + a
}
}
}往返属性
一种强大的模式是往返:先编码再解码(或连续反转两次)后,对于任意输入,都应返回原始值。
import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers
import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks
class RevSpec extends AnyFlatSpec with Matchers with ScalaCheckPropertyChecks {
"reverse" should "be its own inverse" in {
forAll { (xs: List[Int]) =>
xs.reverse.reverse shouldBe xs
}
}
}生成器
ScalaCheck 会自动为内置类型提供 Gen 生成器。您也可以构建自定义生成器,例如使用 Gen.choose(1, 100) 生成有界整数。
import org.scalacheck.Gen
val smallInt: Gen[Int] = Gen.choose(1, 100)
val nonEmpty: Gen[List[Int]] = Gen.nonEmptyListOf(Gen.choose(0, 9))使用自定义生成器
将生成器传递给 forAll 以限制输入,例如只生成正数。
import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers
import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks
import org.scalacheck.Gen
class PosSpec extends AnyFlatSpec with Matchers with ScalaCheckPropertyChecks {
"abs" should "stay positive" in {
forAll(Gen.choose(1, 1000)) { (n: Int) =>
math.abs(n) should be > 0
}
}
}收缩
当属性失败时,ScalaCheck 会收缩失败的输入,找到仍会失败的最小示例。您得到的不是巨大的随机列表,而是易于调试的最小反例。
条件属性
使用 whenever 将属性限制为满足前置条件的输入。不满足条件的输入会被丢弃。
import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers
import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks
class DivSpec extends AnyFlatSpec with Matchers with ScalaCheckPropertyChecks {
"division" should "hold for nonzero divisors" in {
forAll { (a: Int, b: Int) =>
whenever(b != 0) {
(a / b) * b + (a % b) shouldBe a
}
}
}
}适合测试的属性
有用的属性类别:
- 往返:编码/解码、反转/反转。
- 不变量:排序会保留长度。
- 代数定律:交换律、结合律、单位元。
何时使用
属性测试可以补充示例测试,但不能取代示例测试。对于具有明确数学规则或结构规则的纯函数,可以使用属性测试。对于特定的已知案例和回归问题,应保留针对性的示例测试。
快速检查
检查您对基于属性的测试的了解。
回顾
您学习了使用 ScalaCheck 进行基于属性的测试:
- 声明对所有输入都成立的属性;框架会生成大量输入。
forAll驱动生成;Gen创建自定义输入。whenever添加前置条件;失败会被收缩为最小示例。- 非常适合测试往返、不变量和代数定律。
常见问题解答
「基于属性的测试」课时是免费的吗?
是的 — 「基于属性的测试」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Scala for Backend Engineering & Functional Programming 课程的其余内容,请升级到 CoddyKit PRO。 Scala for Backend Engineering & Functional Programming 课程共包含 4 节课。
「基于属性的测试」这节课中我会学到什么?
ScalaCheck 你通过在浏览器中直接运行的动手代码来练习 Scala for Backend Engineering & Functional Programming,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 Scala for Backend Engineering & Functional Programming 需要有经验吗?
无需任何先前经验。CoddyKit 上的 Scala for Backend Engineering & Functional Programming 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 3 节课,共 4 节。
「基于属性的测试」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 Scala for Backend Engineering & Functional Programming 课中编写并运行代码吗?
能。每节 Scala for Backend Engineering & Functional Programming 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。
此课程中的所有课时
- ScalaTest 风格
- 匹配器
- 基于属性的测试
- 模拟与测试装置