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

模拟与测试装置

测试设置

模拟与测试装置 是 CoddyKit 上的免费 Scala for Backend Engineering & Functional Programming 课时。 这是第 4 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Scala for Backend Engineering & Functional Programming 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Scala for Backend Engineering & Functional Programming 课程共包含 4 节课。

测试设置与清理

许多测试需要共享的测试夹具:在测试前准备好的数据或资源,以及测试后需要清理的内容。ScalaTest 提供了多种管理方式,无需重复编写设置代码。

简单夹具:只需一个方法

最简单的夹具是一个用于构建全新状态的辅助方法。每个测试都调用它,从而保证隔离。

import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers

class StackSpec extends AnyFlatSpec with Matchers {
  def emptyStack(): scala.collection.mutable.Stack[Int] =
    scala.collection.mutable.Stack.empty[Int]

  "a stack" should "start empty" in {
    emptyStack().size shouldBe 0
  }
}

BeforeAndAfter

混入 BeforeAndAfter,即可使用 before { } 和 after { } 代码块,在每个测试前后运行代码。

import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers
import org.scalatest.BeforeAndAfter

class DbSpec extends AnyFlatSpec with Matchers with BeforeAndAfter {
  var counter = 0
  before { counter = 10 }
  after  { counter = 0 }

  "counter" should "be initialized" in {
    counter shouldBe 10
  }
}

BeforeAndAfterAll

对于测试套件中所有测试共享的昂贵资源(例如数据库连接或嵌入式服务器),请将 BeforeAndAfterAll 与 beforeAll 和 afterAll 一起使用。

import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.BeforeAndAfterAll

class ServerSpec extends AnyFlatSpec with BeforeAndAfterAll {
  override def beforeAll(): Unit = println("start server")
  override def afterAll(): Unit  = println("stop server")

  "server" should "respond" in {
    assert(true)
  }
}

借用夹具模式

借用模式将新构建的资源传递给测试主体,并在 finally 中保证清理,即使测试失败也不例外。

import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers

class FileSpec extends AnyFlatSpec with Matchers {
  def withBuffer(test: StringBuilder => Any): Unit = {
    val buf = new StringBuilder("init")
    try test(buf)
    finally buf.clear()
  }

  "a buffer" should "start with init" in withBuffer { buf =>
    buf.toString shouldBe "init"
  }
}

为什么要使用模拟

模拟对象是真实依赖项(例如数据库或 HTTP 客户端)的替代品,可以让单元测试保持快速且具有确定性。您可以控制模拟对象返回的内容,并验证它被如何调用。

添加 Mockito

ScalaTest 通过 scalatestplus 与 Mockito 集成。添加依赖后,混入 MockitoSugar。

libraryDependencies += "org.scalatestplus" %% "mockito-5-10" % "3.2.18.0" % Test

模拟方法

创建模拟对象,并使用 when(...).thenReturn(...) 为其行为编程。然后,测试会执行依赖该模拟对象的代码。

import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers
import org.scalatestplus.mockito.MockitoSugar
import org.mockito.Mockito.when

trait Repo { def find(id: Int): String }

class RepoSpec extends AnyFlatSpec with Matchers with MockitoSugar {
  "a repo" should "return stubbed value" in {
    val repo = mock[Repo]
    when(repo.find(1)).thenReturn("Alice")
    repo.find(1) shouldBe "Alice"
  }
}

验证交互

除了返回值之外,您还可以使用 verify 验证某个方法是否按预期次数被调用。

import org.scalatest.flatspec.AnyFlatSpec
import org.scalatestplus.mockito.MockitoSugar
import org.mockito.Mockito.{verify, times}

trait Logger { def log(msg: String): Unit }

class LogSpec extends AnyFlatSpec with MockitoSugar {
  "a service" should "log once" in {
    val logger = mock[Logger]
    logger.log("hi")
    verify(logger, times(1)).log("hi")
  }
}

模拟对象、存根与伪实现

术语:

  • 存根:返回预先设定的答案。
  • 模拟对象:还允许您验证交互。
  • 伪实现:轻量级的可运行实现(例如内存中的仓库)。

简单情况下优先使用伪实现或真实对象;只有真正的外部依赖才使用模拟对象。

保持测试隔离

夹具和模拟对象的目标相同:让每个测试都在已知且独立的状态下运行。在每个测试前重置共享的可变状态,并避免在测试之间共享可变夹具,以防止出现不稳定且依赖执行顺序的失败。

快速检查

检查您对夹具和模拟的了解。

回顾

您学习了夹具和模拟:

  • 夹具:辅助方法、BeforeAndAfter、BeforeAndAfterAll 以及借用模式。
  • 模拟对象(通过 MockitoSugar):使用 when().thenReturn() 创建存根,使用 verify 检查调用。
  • 简单情况下优先使用伪实现;对外部依赖使用模拟对象。
  • 确保每个测试相互隔离且不依赖执行顺序。

常见问题解答

「模拟与测试装置」课时是免费的吗?

是的 — 「模拟与测试装置」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Scala for Backend Engineering & Functional Programming 课程的其余内容,请升级到 CoddyKit PRO。 Scala for Backend Engineering & Functional Programming 课程共包含 4 节课。

「模拟与测试装置」这节课中我会学到什么?

测试设置 你通过在浏览器中直接运行的动手代码来练习 Scala for Backend Engineering & Functional Programming,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 Scala for Backend Engineering & Functional Programming 需要有经验吗?

无需任何先前经验。CoddyKit 上的 Scala for Backend Engineering & Functional Programming 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 4 节课,共 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