1. sbtプラグインのテスト

sbtプラグインのテスト 

テストについて話しましょう。プラグインを作成すると、それは長期的なものになります。新しい機能を追加したり(またはバグを修正したり)し続けるために、テストを書くことは理にかなっています。

scriptedテストフレームワーク 

sbtにはscriptedテストフレームワークが付属しており、ビルドシナリオをスクリプト化できます。これは、変更検出や部分コンパイルなど、複雑なシナリオでsbt自体をテストするために書かれました。

さて、B.scalaを削除したが、A.scalaを更新しないとどうなるかを考えてみましょう。再コンパイルすると、Aが参照するBが存在しなくなったため、エラーが発生するはずです。[… (本当に複雑なもの)]

scriptedテストフレームワークは、上記のケースのようにsbtがケースを処理することを検証するために使用されます。

このフレームワークは、scripted-pluginを介して利用可能になります。このページの残りの部分では、scripted-pluginをプラグインに含める方法について説明します。

ステップ1:スナップショット 

開始する前に、バージョンを**-SNAPSHOT**のものに設定してください。scripted-pluginはプラグインをローカルに公開するためです。SNAPSHOTを使用しないと、あなたと世界中の他の人が異なるアーティファクトを見るという、ひどい一貫性のない状態に陥る可能性があります。

ステップ2:SbtPlugin 

build.sbtSbtPluginを有効にする

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
  )

**注**:SbtPluginを使用するには、sbt 1.2.1以降を使用する必要があります。

ステップ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. #は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/)
...

Enterキーを押すまでテストを中断するには、次の行をtestスクリプトに追加します。

$ 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-pluginおよびsbt-assemblyにも、いくつかのスクリプトテストがあります。

以上です!プラグインのテストに関する経験を教えてください!