Scala 3 — 书籍

使用 sbt 构建和测试 Scala 项目

语言

在本节中,你将看到 Scala 项目中常用的两个工具

我们将首先展示如何使用 sbt 来构建你的 Scala 项目,然后我们将展示如何将 sbt 和 ScalaTest 一起用于测试你的 Scala 项目。

如果你想了解有关帮助你将 Scala 2 代码迁移到 Scala 3 的工具,请参阅我们的 Scala 3 迁移指南

使用 sbt 构建 Scala 项目

你可以使用多种不同的工具来构建你的 Scala 项目,包括 Ant、Maven、Gradle、Mill 等。但一个名为sbt 的工具是第一个专门为 Scala 创建的构建工具。

要安装 sbt,请参阅 下载页面 或我们的 入门 页面。

创建“Hello, world”项目

只需几个步骤,您就可以创建一个 sbt “Hello, world”项目。首先,创建一个工作目录,并移至该目录

$ mkdir hello
$ cd hello

在目录 hello 中,创建一个子目录 project

$ mkdir project

在目录 project 中创建一个名为 build.properties 的文件,内容如下

sbt.version=1.6.1

然后在项目根目录中创建一个名为 build.sbt 的文件,其中包含此行

scalaVersion := "3.4.1"

现在创建一个名为 Hello.scala 的文件(名称的第一部分无关紧要),其中包含此行

@main def helloWorld = println("Hello, world")

您只需执行以上操作即可。

您的项目结构应如下所示

$ tree
.
├── build.sbt
├── Hello.scala
└── project
    └── build.properties

现在使用此 sbt 命令运行项目

$ sbt run

您应该看到类似以下内容的输出,包括程序中的 "Hello, world"

$ sbt run
[info] welcome to sbt 1.5.4 (AdoptOpenJDK Java 11.x)
[info] loading project definition from project ...
[info] loading settings for project from build.sbt ...
[info] compiling 1 Scala source to target/scala-3.0.0/classes ...
[info] running helloWorld
Hello, world
[success] Total time: 2 s

sbt 启动器(sbt 命令行工具)加载 project/build.properties 文件中设置的 sbt 版本,该版本加载 build.sbt 文件中设置的 Scala 编译器版本,编译 Hello.scala 文件中的代码,并运行生成的字节码。

当您查看目录时,您会看到 sbt 有一个名为 target 的目录。这些是 sbt 使用的工作目录。

如您所见,使用 sbt 创建和运行一个小型 Scala 项目只需几个简单的步骤。

在大型项目中使用 sbt

对于一个小项目,sbt 运行仅需要这些。对于需要许多源代码文件、依赖项或 sbt 插件的大型项目,您需要创建一个有组织的目录结构。本节的其余部分演示了 sbt 使用的结构。

sbt 目录结构

与 Maven 类似,sbt 使用标准项目目录结构。这样做的好处是,一旦您熟悉其结构,就可以轻松处理其他 Scala/sbt 项目。

首先要知道的是,在项目的根目录下,sbt 预期有一个目录结构,如下所示

.
├── build.sbt
├── project/
│   └── build.properties
├── src/
│   ├── main/
│   │   ├── java/
│   │   ├── resources/
│   │   └── scala/
│   └── test/
│       ├── java/
│       ├── resources/
│       └── scala/
└── target/

如果您想将非托管依赖项(JAR 文件)添加到项目中,您还可以在根目录下添加一个 lib 目录。

如果您要创建一个包含 Scala 源代码文件和测试的项目,但不会使用任何 Java 源代码文件,并且不需要任何“资源”(例如嵌入式图像、配置文件等),那么这正是您在 src 目录下真正需要的所有内容

.
└── src/
    ├── main/
    │   └── scala/
    └── test/
        └── scala/

带有 sbt 目录结构的“Hello, world”

创建此目录结构很简单。有工具可以为您执行此操作,但假设您使用的是 Unix/Linux 系统,您可以使用以下命令创建您的第一个 sbt 项目目录结构

$ mkdir HelloWorld
$ cd HelloWorld
$ mkdir -p src/{main,test}/scala
$ mkdir project target

在运行这些命令后运行 find . 命令时,您应该看到以下结果

$ find .
.
./project
./src
./src/main
./src/main/scala
./src/test
./src/test/scala
./target

如果您看到这一点,那么您已经为下一步做好了充分的准备。

还有其他方法可以为 sbt 项目创建文件和目录。一种方法是使用 sbt new 命令,该命令在 scala-sbt.org 上有记录。此处未显示该方法,因为它创建的一些文件比此类介绍所需的更复杂。

创建第一个 build.sbt 文件

此时,您只需要再做两件事即可运行“Hello, world”项目

  • 一个 build.sbt 文件
  • 一个 Hello.scala 文件

对于像这样的一个小项目,build.sbt 文件只需要一个 scalaVersion 条目,但我们将添加三行,这是您经常看到的

name := "HelloWorld"
version := "0.1"
scalaVersion := "3.4.1"

由于 sbt 项目使用标准目录结构,因此 sbt 可以找到它需要的所有其他内容。

现在您只需要添加一个小型的“Hello, world”程序。

一个“Hello, world”程序

在大型项目中,您所有的 Scala 源代码文件都将位于 src/main/scalasrc/test/scala 目录下,但对于像这样的一个小示例项目,您可以将源代码文件放在项目的根目录中。因此,在根目录中创建一个名为 HelloWorld.scala 的文件,其中包含以下内容

@main def helloWorld = println("Hello, world")

该代码定义了一个 Scala 3 “main”方法,该方法在运行时打印 "Hello, world"

现在,使用 sbt run 命令编译并运行您的项目

$ sbt run

[info] welcome to sbt
[info] loading settings for project ...
[info] loading project definition
[info] loading settings for project root from build.sbt ...
[info] Compiling 1 Scala source ...
[info] running helloWorld 
Hello, world
[success] Total time: 4 s

首次运行 sbt 时,它会下载所需的一切,这可能需要几分钟的时间,但之后会变得快很多。

此外,一旦你让这第一步正常运行,你就会发现以交互方式运行 sbt 会快很多。要做到这一点,首先单独运行 sbt 命令

$ sbt

[info] welcome to sbt
[info] loading settings for project ...
[info] loading project definition ...
[info] loading settings for project root from build.sbt ...
[info] sbt server started at
       local:///${HOME}/.sbt/1.0/server/7d26bae822c36a31071c/sock
sbt:hello-world> _

然后在这个 sbt shell 中,执行其 run 命令

sbt:hello-world> run

[info] running helloWorld 
Hello, world
[success] Total time: 0 s

这样快多了。

如果你在 sbt 命令提示符处输入 help,你将看到可以运行的其他命令列表。但现在,只需输入 exit(或按 CTRL-D)即可退出 sbt shell。

使用项目模板

手动创建项目结构可能会很繁琐。谢天谢地,sbt 可以根据模板为你创建它。

要从模板创建 Scala 3 项目,请在 shell 中运行以下命令

$ sbt new scala/scala3.g8

Sbt 将加载模板,询问一些问题,并在子目录中创建项目文件

$ tree scala-3-project-template 
scala-3-project-template
├── build.sbt
├── project
│   └── build.properties
├── README.md
└── src
    ├── main
    │   └── scala
    │       └── Main.scala
    └── test
        └── scala
            └── Test1.scala

如果你想创建一个与 Scala 2 交叉编译的 Scala 3 项目,请使用模板 scala/scala3-cross.g8

$ sbt new scala/scala3-cross.g8

sbt 文档 中了解有关 sbt new 和项目模板的更多信息。

Scala 的其他构建工具

虽然 sbt 被广泛使用,但还有其他工具可用于构建 Scala 项目

Coursier

相关说明,Coursier 是一个“依赖解析器”,在功能上类似于 Maven 和 Ivy。它完全用 Scala 编写,“采用函数式编程原则”,并并行下载工件以实现快速下载。sbt 使用它来处理大多数依赖解析,并且作为一个命令行工具,它可以轻松地在你的系统上安装 sbt、Java 和 Scala 等工具,如我们在 入门 页面中所示。

来自 launch 网页的此示例显示 cs launch 命令可用于从依赖项启动应用程序

$ cs launch org.scalameta::scalafmt-cli:2.4.2 -- --help
scalafmt 2.4.2
Usage: scalafmt [options] [<file>...]

  -h, --help               prints this usage text
  -v, --version            print version
  more ...

有关更多详细信息,请参阅 Coursier 的 启动页面

将 sbt 与 ScalaTest 一起使用

ScalaTest 是 Scala 项目的主要测试库之一。在本节中,你将看到创建使用 ScalaTest 的 Scala/sbt 项目所需步骤。

1) 创建项目目录结构

与上一课一样,使用以下命令为名为 HelloScalaTest 的项目创建一个 sbt 项目目录结构

$ mkdir HelloScalaTest
$ cd HelloScalaTest
$ mkdir -p src/{main,test}/scala
$ mkdir project

2) 创建 build.properties 和 build.sbt 文件

接下来,在项目的 project/ 子目录中使用此行创建一个 build.properties 文件

sbt.version=1.5.4

接下来,在项目的根目录中使用以下内容创建一个 build.sbt 文件

name := "HelloScalaTest"
version := "0.1"
scalaVersion := "3.4.1"

libraryDependencies ++= Seq(
  "org.scalatest" %% "scalatest" % "3.2.9" % Test
)

此文件的前三行与第一个示例基本相同。 libraryDependencies 行告诉 sbt 包含包含 ScalaTest 所需的依赖项(JAR 文件)。

ScalaTest 文档一直很好,您始终可以在 安装 ScalaTest 页面上找到这些行应如何显示的最新信息。

3) 创建 Scala 源代码文件

接下来,创建一个 Scala 程序,可用于演示 ScalaTest。首先,在 src/main/scala 下创建一个名为 math 的目录

$ mkdir src/main/scala/math
            ----

然后,在该目录中,创建一个名为 MathUtils.scala 的文件,其中包含以下内容

package math

object MathUtils:
  def double(i: Int) = i * 2

该方法提供了一种演示 ScalaTest 的简单方法。

4) 创建您的第一个 ScalaTest 测试

ScalaTest 非常灵活,并提供了多种编写测试的方法。入门的一种简单方法是使用 ScalaTest AnyFunSuite 编写测试。首先,在 src/test/scala 目录下创建一个名为 math 的目录

$ mkdir src/test/scala/math
            ----

接下来,在该目录中创建一个名为 MathUtilsTests.scala 的文件,其中包含以下内容

package math
  
import org.scalatest.funsuite.AnyFunSuite

class MathUtilsTests extends AnyFunSuite:

  // test 1
  test("'double' should handle 0") {
    val result = MathUtils.double(0)
    assert(result == 0)
  }

  // test 2
  test("'double' should handle 1") {
    val result = MathUtils.double(1)
    assert(result == 2)
  }
 
  test("test with Int.MaxValue") (pending)

end MathUtilsTests

此代码演示了 ScalaTest AnyFunSuite 方法。几个要点

  • 您的测试类应扩展 AnyFunSuite
  • 通过为每个 test 赋予唯一名称,按所示方式创建测试
  • 在每个测试的末尾,您应调用 assert 来测试是否满足条件
  • 当您知道要编写测试但不想立即编写时,请使用所示语法将测试创建为“挂起”

像这样使用 ScalaTest 类似于 JUnit,因此如果您从 Java 转到 Scala,希望这看起来很相似。

现在,您可以使用 sbt test 命令运行这些测试。跳过输出的前几行,结果如下所示

sbt:HelloScalaTest> test

[info] Compiling 1 Scala source ...
[info] MathUtilsTests:
[info] - 'double' should handle 0
[info] - 'double' should handle 1
[info] - test with Int.MaxValue (pending)
[info] Total number of tests run: 2
[info] Suites: completed 1, aborted 0
[info] Tests: succeeded 2, failed 0, canceled 0, ignored 0, pending 1
[info] All tests passed.
[success] Total time: 1 s

如果一切正常,您将看到类似这样的输出。欢迎来到使用 sbt 和 ScalaTest 测试 Scala 应用程序的世界。

支持多种类型的测试

此示例演示了一种类似于 xUnit 测试驱动开发 (TDD) 样式测试的测试样式,并具有行为驱动开发 (BDD) 样式的一些优点。

如前所述,ScalaTest 非常灵活,您还可以使用其他样式编写测试,例如类似于 Ruby 的 RSpec 的样式。您还可以使用模拟对象、基于属性的测试,并使用 ScalaTest 测试 Scala.js 代码。

请参阅 ScalaTest 网站 上的用户指南,以了解有关不同可用测试样式的更多详细信息。

从这里开始

有关 sbt 和 ScalaTest 的更多信息,请参阅以下资源

此页面的贡献者