0Pricing
Scala for Backend Engineering & Functional Programming · 课时

基于属性的测试

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" % Test

forAll

核心构造是 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 反馈 — 无需本地设置。

此课程中的所有课时

  1. ScalaTest 风格
  2. 匹配器
  3. 基于属性的测试
  4. 模拟与测试装置
← 返回 Scala for Backend Engineering & Functional Programming