1. 測試 sbt 插件

測試 sbt 插件 

讓我們來談談測試。一旦你編寫了一個插件,它就會變成一個長期的項目。為了持續添加新功能(或持續修復錯誤),編寫測試是合理的。

腳本化測試框架 

sbt 附帶了腳本化測試框架,可讓你編寫建置情境的腳本。它被編寫用於在複雜情境下測試 sbt 本身 — 例如變更偵測和部分編譯

現在,考慮一下,如果你刪除 B.scala 但不更新 A.scala 會發生什麼。當你重新編譯時,應該會收到錯誤,因為 B 不再存在供 A 參考。[…(非常複雜的東西)]

腳本化測試框架用於驗證 sbt 是否能處理如上所述的情況。

該框架透過 scripted-plugin 提供。本頁的其餘部分將說明如何將 scripted-plugin 包含到你的插件中。

步驟 1:快照 

在你開始之前,將你的版本設定為 **-SNAPSHOT** 版本,因為 scripted-plugin 會在本地發佈你的插件。如果你不使用 SNAPSHOT,你可能會陷入你和世界其他地方看到不同成品的可怕不一致狀態。

步驟 2:SbtPlugin 

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

步驟 3:src/sbt-test 

建立目錄結構 src/sbt-test/<測試群組>/<測試名稱>。首先,嘗試類似 src/sbt-test/<你的插件名稱>/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")
  }
}

步驟 4:編寫腳本 

現在,編寫一個腳本,在位於測試專案根目錄的稱為 test 的檔案中描述你的情境。

# check if the file gets created
> assembly
$ exists target/scala-2.10/foo.jar

以下是腳本的語法

  1. # 開始單行註解
  2. > name 將任務傳送到 sbt(並測試是否成功)
  3. $ name arg* 執行檔案命令(並測試是否成功)
  4. -> name 將任務傳送到 sbt,但預期它會失敗
  5. -$ name arg* 執行檔案命令,但預期它會失敗

檔案命令有

  • touch path+ 建立或更新檔案的時間戳記
  • delete path+ 刪除檔案
  • exists path+ 檢查檔案是否存在
  • mkdir path+ 建立目錄
  • absent path+ 檢查檔案是否不存在
  • newer source target 檢查 source 是否較新
  • must-mirror source target 檢查 source 是否相同
  • pause 暫停直到按下 Enter 鍵
  • sleep time 睡眠(以毫秒為單位)
  • exec command args* 在另一個進程中執行命令
  • copy-file fromPath toPath 複製檔案
  • copy fromPath+ toDir 將路徑複製到 toDir,保留相對結構
  • copy-flat fromPath+ toDir 將路徑複製到 toDir 平面化

因此,我的腳本將執行 assembly 任務,並檢查是否建立 foo.jar。我們稍後將介紹更複雜的測試。

步驟 5:執行腳本 

若要執行腳本,請返回你的插件專案,然後執行

> scripted

這會將你的測試建置複製到暫時目錄中,並執行 test 腳本。如果一切順利,你會看到 publishLocal 正在執行,然後

Running sbt-assembly / simple
[success] Total time: 18 s, completed Sep 17, 2011 3:00:58 AM

步驟 6:自訂斷言 

檔案命令很棒,但遠遠不夠,因為它們都無法測試實際內容。測試內容的簡單方法是在你的測試建置中實作自訂任務。

對於我的 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

步驟 7:測試測試 

在你掌握它之前,測試本身可能需要一段時間才能正確運作。有幾種技術可能會派上用場。

首先要開始的地方是關閉日誌緩衝。

> 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 腳本,以暫停測試直到你按下 Enter 鍵

$ pause

如果你正在考慮進入 sbt/sbt-test/sbt-foo/simple 並執行 sbt,請不要這麼做。正確的方法是將目錄複製到其他地方並執行它。

步驟 8:獲得靈感 

在 sbt 專案本身下,實際上還有 100 多個腳本化測試。瀏覽一下以獲得靈感。

例如,以下是名為 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-pluginsbt-assembly 也有一些腳本化測試。

就是這樣!請告訴我您測試插件的經驗!