让我们谈谈测试。一旦你编写了插件,它就变成了一个长期的事情。为了不断添加新功能(或修复 bug),编写测试是有意义的。
sbt 带有一个 scripted 测试框架,允许你编写一个构建场景的脚本。它是用来测试 sbt 本身在复杂场景下的行为,比如变更检测和部分编译。
现在,假设你删除了 B.scala,但没有更新 A.scala。当你重新编译时,你应该会收到一个错误,因为 A 无法再引用 B。[...(非常复杂的东西)]
scripted 测试框架用于验证 sbt 能够处理像上面描述的这种场景。
该框架通过 scripted-plugin 提供。本页面的其余部分介绍如何将 scripted-plugin 包含到你的插件中。
在开始之前,将你的版本设置为 -SNAPSHOT 版本,因为 scripted-plugin 会将你的插件发布到本地。如果你不使用 SNAPSHOT,你可能会遇到一个糟糕的不一致状态,即你和其他用户看到不同的工件。
在 build.sbt
中启用 SbtPlugin
。
lazy val root = (project in file("."))
.enablePlugins(SbtPlugin)
.settings(
name := "sbt-something"
)
然后将以下设置添加到 build.sbt
lazy val root = (project in file("."))
.enablePlugins(SbtPlugin)
.settings(
name := "sbt-something",
scriptedLaunchOpts := { scriptedLaunchOpts.value ++
Seq("-Xmx1024M", "-Dplugin.version=" + version.value)
},
scriptedBufferLog := false
)
注意:你必须使用 sbt 1.2.1 及更高版本才能使用 SbtPlugin
。
创建目录结构 src/sbt-test/<test-group>/<test-name>
。为了开始,尝试使用类似于 src/sbt-test/<your-plugin-name>/simple
的结构。
现在准备好了吗?在 simple
中创建一个初始构建。就像使用你的插件的真实构建一样。我相信你已经有一些手动测试的构建。以下是一个 build.sbt
示例。
lazy val root = (project in file("."))
.settings(
version := "0.1",
scalaVersion := "2.10.6",
assembly / assemblyJarName := "foo.jar"
)
在 project/plugins.sbt
中
sys.props.get("plugin.version") match {
case Some(x) => addSbtPlugin("com.eed3si9n" % "sbt-assembly" % x)
case _ => sys.error("""|The system property 'plugin.version' is not defined.
|Specify this property using the scriptedLaunchOpts -D.""".stripMargin)
}
这是一个我从 earldouglas/xsbt-web-plugin@feabb2 学到的技巧,它允许我们将版本号传递到测试中。
我还有 src/main/scala/hello.scala
object Main {
def main(args: Array[String]): Unit = {
println("hello")
}
}
现在,编写一个脚本,在一个名为 test
的文件中描述你的场景,该文件位于你的测试项目的根目录。
# check if the file gets created
> assembly
$ exists target/scala-2.10/foo.jar
以下是脚本的语法
#
开始单行注释>
name
将任务发送到 sbt(并测试它是否成功)$
name arg*
执行文件命令(并测试它是否成功)->
name
将任务发送到 sbt,但预期会失败-$
name arg*
执行文件命令,但预期会失败文件命令包括
touch
path+
创建或更新文件的最后修改时间delete
path+
删除文件exists
path+
检查文件是否存在mkdir
path+
创建目录absent
path+
检查文件是否不存在newer
source target
检查 source
是否比 target
更新must-mirror
source target
检查 source
是否与 target
相同pause
暂停,直到按下回车键sleep
time
休眠(以毫秒为单位)exec
command args*
在另一个进程中运行命令copy-file
fromPath toPath
复制文件copy
fromPath+ toDir
将路径复制到 toDir
,保留相对结构copy-flat
fromPath+ toDir
将路径复制到 toDir
,扁平化结构因此我的脚本将运行 assembly
任务,并检查 foo.jar
是否被创建。我们将在后面介绍更复杂的测试。
要运行脚本,回到你的插件项目,并运行
> scripted
这将把你的测试构建复制到一个临时目录,并执行 test
脚本。如果一切顺利,你将看到 publishLocal
正在运行,然后
Running sbt-assembly / simple
[success] Total time: 18 s, completed Sep 17, 2011 3:00:58 AM
文件命令很好,但还远远不够,因为它们没有测试实际内容。测试内容的一种简单方法是在你的测试构建中实现一个自定义任务。
对于我的 hello 项目,我想检查生成的 jar 是否打印出 "hello"。我可以利用 scala.sys.process.Process
来运行 jar。要表达失败,只需抛出错误。以下为 build.sbt
import scala.sys.process.Process
lazy val root = (project in file("."))
.settings(
version := "0.1",
scalaVersion := "2.10.6",
assembly / assemblyJarName := "foo.jar",
TaskKey[Unit]("check") := {
val process = Process("java", Seq("-jar", (crossTarget.value / "foo.jar").toString))
val out = (process!!)
if (out.trim != "bye") sys.error("unexpected output: " + out)
()
}
)
我故意测试它是否匹配 "bye",以查看测试是如何失败的。
以下是 test
# check if the file gets created
> assembly
$ exists target/foo.jar
# check if it says hello
> check
运行 scripted
按照预期失败了测试
[info] [error] {file:/private/var/folders/Ab/AbC1EFghIj4LMNOPqrStUV+++XX/-Tmp-/sbt_cdd1b3c4/simple/}default-0314bd/*:check: unexpected output: hello
[info] [error] Total time: 0 s, completed Sep 21, 2011 8:43:03 PM
[error] x sbt-assembly / simple
[error] {line 6} Command failed: check failed
[error] {file:/Users/foo/work/sbt-assembly/}default-373f46/*:scripted: sbt-assembly / simple failed
[error] Total time: 14 s, completed Sep 21, 2011 8:00:00 PM
在你习惯之前,可能需要一段时间才能让测试本身正确运行。有一些技巧可能会派上用场。
首先要做的就是关闭日志缓冲。
> set scriptedBufferLog := false
例如,这应该会打印出临时目录的位置
[info] [info] Set current project to default-c6500b (in build file:/private/var/folders/Ab/AbC1EFghIj4LMNOPqrStUV+++XX/-Tmp-/sbt_8d950687/simple/project/plugins/)
...
将以下行添加到你的 test
脚本中,以暂停测试,直到你按下回车键
$ pause
如果你正在考虑进入 sbt/sbt-test/sbt-foo/simple
并运行 sbt
,请不要这样做。正确的方法是将目录复制到其他地方并运行它。
在 sbt 项目本身中,有 100 多个 scripted 测试。浏览它们以获取灵感。
例如,这里有一个名为 by-name 的测试。
> compile
# change => Int to Function0
$ copy-file changes/A.scala A.scala
# Both A.scala and B.scala need to be recompiled because the type has changed
-> compile
xsbt-web-plugin 和 sbt-assembly 也有一些 scripted 测试。
就这样!请告诉我你测试插件的经验!