mike-neckのブログ

Java or Groovy or Swift or Golang

Gradle TestKit用のプラグインをリリースしました

f:id:mike_neck:20150818084108g:plain

表記のとおりです。

動機

Gradle2.6からの機能の一つgradleTestKitはかなり便利なのですが、まだまだ機能不足で、特に自作プラグインを読み込めないところが弱点であると言ってきました。

mike-neck.hatenadiary.com

mike-neck.hatenadiary.com

mike-neck.hatenadiary.com

Gradle Forumで、質問してもやっぱり、そのあたりの機能はないんだよねーと言われています。

discuss.gradle.org

まあ、やり方はわかっていたので、それらを自動でやってくれるようなクラスを作っちゃえばいいんだろうということで、自動で自作プラグインを読み込むクラスを生成するタスクを生やすプラグインを作りました。

Gradle - Plugin: org.mikeneck.gradle-testkit-support-plugin

使い方

プラグインの読み込み

プラグインを読み込みます。なお、Gradle2.6以上(2.5でも動きますが、TestKit自体は2.6からの機能なので2.5以下で使用しても無意味だし、コンパイルエラーになります)、Java8以上でないと動きません。また、configuration.runtimeを利用するので、javaもしくはgroovyプラグインが必要になります(プラグインを作る場合はいずれかを利用するので問題無いとおもいますが…)。

plugins {
    id 'groovy'
    id "org.mikeneck.gradle-testkit-support-plugin" version "0.1"
}

テスト依存性の設定

gradleTestKitに依存しますので、必ずtestCompilegradleTestKit()を指定して下さい。あとSpockを使うと幸せになります。

dependencies {
    compile 'org.slf4j:slf4j-api:1.7.12'
    compile 'com.owlike:genson:1.3'

    compile gradleApi()

    // gradleTestKitを必ず使うこと
    testCompile gradleTestKit()
    testCompile ('org.spockframework:spock-core:1.0-groovy-2.3') {
        exclude module: 'groovy-all'
    }
}

モデルの指定

モデルを指定します。

model{}ブロックの中にtestKitSupport{}ブロックが生えていますので、その中で下記の3つの値を設定してください。

プロパティ 設定すべき値 デフォルト値
testSrcDir String 生成されるソースの出力ソースセット src/test/java
packageName String パッケージ名 未指定なので必ず指定してください
className String 生成されるクラスの名前 TestProject

コード例

model {
    testKitSupport {
        testSrcDir = 'src/test/groovy'
        packageName = 'com.sample.plugin'
        className = 'TestProject'
    }
}

コードの生成

モデルが正しく定義されていると、generateTestKitSupportというタスクが実行可能になりますので、これを実行してください。

$ gradle --daemon gTKS
:storeModelForTestKitSupport
:generateTestKitSupport

BUILD SUCCESS

Total time: 5.020 sec
$

こんな感じで表示されるでしょう。

これによって、com.sample.plugin.TestProjectというクラスが生成されます。

テストでの使い方

生成されたクラスはExternalResourceクラスを継承したクラスで、内部的にはTemporaryFolderクラスを利用したクラスになっていますので、JUnitでは@Ruleアノテーションで指定しておくことで機能を利用できます。

JUnitの場合はテストの前後で一時フォルダーが作成され、そこをプロジェクトディレクトリーとして利用することができます。

  • buildGradle(String)というメソッドにビルドスクリプトを渡すことで、build.gradleファイルが生成されます。このbuild.gradleでは自作プラグインクラスすべてとリソース、および依存ライブラリー(Gradle APIを除く)を読み込みます。
  • 渡すスクリプトには自作プラグインプラグインidを読み込むようにしておきます。これでテスト用のプロジェクトに自作プラグインが適用された状態を作り出せます。
  • run(String...)というメソッドがありますので、それに実行したいタスク名を渡します。このメソッドから返されるオブジェクトにはbuild()というメソッドbuildAndFail()というメソッドがありますので、成功を期待する場合はbuild()を、失敗を期待する場合はbuildAndFail()を呼び出してください。
  • buildおよびbuildAndFailメソッドから返されるオブエジェクトからは標準出力、標準エラー出力、個々のタスクの実行結果などが参照可能です。

サンプルコード(Spock)

class YourPluginSpec extends Specification {

    @Rule
    def TestProject testProject = TestProject.target(YourPluginImpl)

    def setup() {
        testProject.ready()
    }

    def cleanup() {
        testProject.end()
    }

    def 'given build script will success'() {
        given:
        def script = """|apply plugin: 'your-plugin-id'
                |model {
                |    yourPlugin {
                |        definition = 'test'
                |    }
                |}
                |""".stripMargin()
        testProject.buildGradle(script)

        when:
        def result = testProject.run('yourTaskName')
                .build()

        then:
        // 標準出力には definition = test が出力される
        result.standardOutput.contains('definition = test')
        // タスクは成功する
        result.task(":yourTaskName").outcome = TaskOutcome.SUCCESS
    }
}

レポジトリー

ここ

github.com

TBD

READMEにも書いてありますが、このプラグインが依存しているrule-based-model-generationプラグインがまだまだ機能が貧弱なため、こちらの改善を進めたいと思っています。