9
1

More than 1 year has passed since last update.

TL; DR

みなさんGroovyという言語をご存知でしょうか?
一言で言うとJVM上で動くRubyみたいな柔軟な構文の言語です。1

Groovyで開発したことはないと言う人でもJenkinsfileやbuild.gradle等でお世話になっている人はそれなりにいるのではないでしょうか?(私もその一人です)

複雑なJenkinsfileやbuild.gradleを書いているとビルドスクリプトのテストやデバッグが大変になってきます。2
Jenkinsジョブやビルドは一回の実行に数分かかることもよくあるので、printfデバッグでは生産性が非常に悪いです。

実はGroovyはユニットテストも簡単に書けてJavaと同じように実行したりデバッグできるのです!

この記事ではIDE(Eclipse)およびCI(GitLab-CI)でGroovyのテストをどうやって実行するのかを紹介します。

セットアップ

GroovyのビルドツールにはGradleを用います。Gradleの設定ファイルもGroovyで書かれていることからもわかるように親和性が高いです。

Gradleのインストール

以下のリンクを参考にダウンロードします。最新バージョンは7.3.1

インストールが終わったらgradle --versionでバージョンを確認しましょう。

gradle --version
------------------------------------------------------------
Gradle 7.3.1
------------------------------------------------------------
Build time:   2021-12-01 15:42:20 UTC
Revision:     2c62cec93e0b15a7d2cd68746f3348796d6d42bd
Kotlin:       1.5.31
Groovy:       3.0.9
Ant:          Apache Ant(TM) version 1.10.11 compiled on July 10 2021
JVM:          1.8.0_282 (Amazon.com Inc. 25.282-b08)
OS:           Windows 10 10.0 amd64

Projectの初期化

gradle initでインタラクティブにgroovyのプロジェクトを作成できます。

> gradle init
Starting a Gradle Daemon (subsequent builds will be faster)
Select type of project to generate:
  1: basic
  2: application
  3: library
  4: Gradle plugin
Enter selection (default: basic) [1..4] 3
Select implementation language:
  1: C++
  2: Groovy
  3: Java
  4: Kotlin
  5: Scala
  6: Swift
Enter selection (default: Java) [1..6] 2
Select build script DSL:
  1: Groovy
  2: Kotlin
Enter selection (default: Groovy) [1..2] 1
Generate build using new APIs and behavior (some features may change in the next minor release)? (default: no) [yes, no]                                                                                                                       yes
Project name (default: groovy-example): groovy-example
Source package (default: groovy.example): example
> Task :init
Get more help with your project: https://docs.gradle.org/7.3.1/samples/sample_building_groovy_libraries.html
BUILD SUCCESSFUL in 1m 27s
2 actionable tasks: 2 executed

ディレクトリ構成の確認

さて、一旦ディレクトリ構成を確認しておきましょう。

.
├── gradle
│   └── wrapper
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── lib
│   ├── build.gradle
│   └── src
│       ├── main
│       │   ├── groovy    # メインソースディレクトリ
│       │   └── resources # メインリソースディレクトリ
│       └── test
│           ├── groovy    # テストソースディレクトリ
│           └── resources # テストリソースディレクトリ
└── settings.gradle

multi-projectのディレクトリ構成となっています。
lib以下が実際のプロジェクトで、src/main/groovyに実際のコードを置いて行きます。

ディレクトリ構成が気に入らない場合は適宜修正してbuild.gradleも修正しましょう。

IDEの設定

GroovyをサポートしているIDEはIntelliJ IDEAとEclipseです。
(VSCodeの良いプラグインがあったら教えてください)

今回はEclipse(21-12)での手順を紹介します。

プラグインのインストール

まずは、以下のGroovyのプラグインをインストールしましょう。

Help > Eclipse Market Placeから groovy development toolsで検索してインストールします。

プロジェクトのインポート

File > Importから Gradle > Existing Gradle Projectを選択します。

image.png

gradle initを実行したディレクトリ(libではないので注意)をプロジェクトルートディレクトリに指定します。

他はデフォルトのままで、完了ボタンを押します。

プロジェクトルートディレクトリと、libディレクトリがそれぞれプロジェクトに表示されていればOKです。
image (1).png

[TroubleShooting] JDKがインストールされていないエラーが出た場合

デフォルトの設定だと JavaSE-17が指定されます。 EclipseにJava17のJDKがインストールされていない場合、エラーになります。

その場合はJDKをインストールするか、あるいはbuild.gradleを修正してJavaのバージョンを下げましょう。

build.gradle
java {
    sourceCompatibility = '11'
    targetCompatibility = '11'
}

実行

テストの実行

テストのファイルを選択して、Run > As JUnit Test(あるいはAlt + Shift + X -> T)を選択するとテストが実行できます。

image (3).png

デバッグ

次はデバッグ方法を試してみましょう。

LibraryTest.groovyの以下の行の左側をダブルクリックし、ブレークポイントを設置します。

def result = lib.someLibraryMethod()

今度はDebug > As JUnit Testから実行してみましょう。

デバッグ画面に切り替わり、ブレークポイントで停止しているはずです。

image (4).png

ステップインして関数の中に入ったり、ステップ実行したりもJavaのデバッグと同じ感覚でできます。

カバレッジ

さらにカバレッジの取得方法も確認しておきましょう。

今度はCoverage > As JUnit Testから実行してみましょう。

テストが実行され、カバレッジが表示されます。(ダークテーマだと見づらいのでライトテーマに変更してスクリーンショットを取得している)

image (5).png

CIの設定

さて、ローカルでテスト実行とカバレッジが取得できるようになったので、今度はCIでも実行できるように設定しましょう。

Gradleでのカバレッジの取得

build.gradleにjacocoプラグインを追加して、XMLのレポートを出力できるようにします。

build.gradle
 plugins {
   id 'groovy'
   id 'java-library'

+  id 'jacoco'
 }

+jacocoTestReport {
+    reports {
+        xml.required = true
+        html.required = false
+    }
+}

そうすると以下のコマンドでテスト結果とカバレッジが出力できるようになります。

gradlew test jacocoTestReport

出力先は以下の通りです。

  • JUnit: build/test-results/test/TEST-*.xml
  • Coverage: build/reports/jacoco/test/jacocoTestReport.xml

CIでの設定

今回はGitLab-CIでの設定をしてみました。
詳しい設定方法はこの辺のドキュメントを読むとわかります。

以下の内容を.gitlab-ci.ymlに保存してレポジトリにコミットするとCIの設定ができます。

.gitlab-ci.yml
groovy-ci:
  image: gradle:7.3.1-jdk17
  script:
    - gradle -i test jacocoTestReport
  variables: # デフォルトだと`$HOME/.gradle`が使われてキャッシュできないのでプロジェクトローカルに変更
    GRADLE_USER_HOME: $CI_PROJECT_DIR/.gradle_home
  cache: # 依存関係のキャッシュ
    key:
      files:
        - lib/build.gradle
    paths:
      - .gradle_home/caches
  needs: [] # GitLab 14.2以降ではstageを省略できます
  artifacts:
    when: always
    reports:
      junit: lib/build/test-results/test/TEST-*.xml
    paths:
      - lib/build/reports/jacoco/test/jacocoTestReport.xml
  rules: # マージリクエストとデフォルトブランチでジョブを実行
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
    - if: $CI_MERGE_REQUEST_IID

report-coverage:
  image: registry.gitlab.com/haynes/jacoco2cobertura:1.0.7
  script:
    - python /opt/cover2cover.py lib/build/reports/jacoco/test/jacocoTestReport.xml $CI_PROJECT_DIR/lib/src/main/groovy > coverage.xml
  needs:
    - groovy-ci
  artifacts:
    reports:
      cobertura: coverage.xml
  rules:
    - if: $CI_MERGE_REQUEST_IID

書き慣れていないと面食らいますが、二つのジョブで行います。

  • groovy-ci: gradleでのテストの実行とカバレッジの取得を行います。
    • artifacts:reportsでJUnitの結果を読み込んでいます。
  • report-coverage: jacoco形式のレポートをGitLabが認識できるcobertura形式に変換します。 
    • artifacts:reportsでカバレッジの結果を読み込んでいます。

試しに、メソッドとテストを追加してみましょう。

Library.groovy
 boolean someLibraryMethod() {
     true
 }

+int max(int a, int b) {
+    if( a < b ) {
+        return b;
+    } else {
+        return a;
+    }
+}
LibraryTest.groovy
 class LibraryTest extends Specification {
 ...
+    def "max 1 2 returns 2"() {
+        setup:
+        def lib = new Library()
+
+        when:
+        def result = lib.max(1, 2)
+
+        then:
+        result == 2
+    }
 }

マージリクエストを作成すると、テスト結果が表示されます。

image (7).png

マージリクエストのdiffタブを開くとカバレッジが表示されます。

then節はカバレッジが通っているがelse節はカバレッジが通っていないことがわかります。

image (6).png

終わりに

今回紹介したように、実はGroovyでも気軽にユニットテストを動かすことができます。
GroovyのテストフレームワークSpockは表現力が高く、
Javaのプロジェクトの単体テストをGroovyで書いてみると言うのもありかもしれません。

9
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
1