倭マン's BLOG

くだらない日々の日記書いてます。 たまにプログラミング関連の記事書いてます。 書いてます。

Spock ことはじめ ~without IDE 編~

Java/Groovy のテスティング・フレームワーク Spock の使い方をあれこれ調べてみました。 まぁ、Gradle 使えるならそれに越したことはないですが、なんとなく Maven3 やコマンドラインからの使用方法も試してみました。

どうも Maven3 から GMaven が Groovy コードのコンパイルをサポートしなくなったようで(Groovy スクリプトの実行などに機能を絞ったもよう)、Spock のドキュメントに書いてある方法では Maven3 上で Spock を使えなくなってます。 Maven3 での Groovy コードのコンパイルは Groovy Eclipse compiler プラグインというのを使って行うようです。 このプラグインは Maven3 の Java コンパイラを Groovy もコンパイルできるものに置き換えるというアプローチの Groovy サポートなようです。 まぁ、Maven3 上で Spock を使うってのにどの程度のニーズがあるか分かりませんが。

それ以上に使うかどうか分かりませんが、Gradle や Maven3 のようなビルドツール(プロジェクト管理ツール)を使わずに groovy コマンドで Spock テストを行うことも出来ます。

他に Apache Ant を使ったビルド方法もあるようですが、build.xml を読むのが面倒だったのでパス。 ご興味のある方はこちらを参照。

目次

参考

Gradle

まずは王道の Gradle による Spock テスト。 プロジェクトのディレクトリ構造が以下のようになっているとします:

  • ${project.rootDir}
    • build.gradle
    • src
      • main
        • java
        • groovy
      • test
        • groovy
          • my
            • test
              • HelloSpock.groovy

ファイルは build.gradle と HelloSpock.groovy のみ。 build.gradle はプロジェクトのルートディレクトリに、HelloSpock.groovy は my.test パッケージに配置しています。 まぁ、普通の Gradle プロジェクトですね。 プロジェクトのテストに Spock を使うには、build.gradle に以下のような設定を書きます(ちょっと余計な設定を入れてますが):

// build.gradle
apply plugin: "groovy"

group = "my.test"
version = "1.0-SNAPSHOT"
sourceCompatibility = 1.8
targetCompatibility = 1.8
tasks.withType(Compile){ options.encoding = "UTF-8" }

repositories.mavenCentral()

dependencies{
    compile "org.codehaus.groovy:groovy-all:2.3.0-rc-1"
    testCompile "org.spockframework:spock-core:0.7-groovy-2.0"
}

task wrapper(type: Wrapper) {
    gradleVersion = "1.12"
}

大事なのは「apply plugin : "groovy"」と dependency ノード下の依存性の設定です。 Gradle のちょっと前のバージョンから、Groovy の依存性は groovy ではなく compile によって設定するようになりましたね*1

次は Spock によるテストコード HelloSpock.groovy。 Spock wiki 「HelloSpock」から拝借しました(パッケージ宣言だけ追加しています):

// HelloSpock.groovy
package my.test

import spock.lang.*

class HelloSpock extends Specification{
    def "length of Spock's and his friends' names"(){
        expect:
        name.size() == length

        where:
        name     | length
        "Spock"  | 5
        "Kirk"   | 4
        "Scotty" | 6
    }
}

これで準備 OK。 次はテストの実行。

テストの実行
上記のようにプロジェクトが用意できたら、次はテストの実行。 プロジェクトのルートディレクトリで以下のようにコマンドを実行します*2

gradle test

実行結果は

  • ${project.rootDir}/build/reports/tests/index.html

から確認できます。 コマンドライン上で確認できないのは少々面倒。 まぁ、失敗が多いとブラウザで構造的に閲覧できないと手に負えなくなりますがね。

テストのフィルタリング
Gradle の最後は、1つもしくは一部のテストを指定して実行する方法。 Gradle User Guide 「The Java Plugin : Test」 の Test filtering の箇所を参照。 「--tests」オプションを使用して、クラスやメソッド (feature) を完全修飾名で指定したり、ワイルドカード「*」で適合するものを指定したりできます:

  • gradle test --tests org.gradle.SomeTest.someSpecificFeature
  • gradle test --tests *SomeTest.someSpecificFeature
  • gradle test --tests *SomeSpecificTest
  • gradle test --tests all.in.specific.package*
  • gradle test --tests *IntegTest
  • gradle test --tests *IntegTest*ui*
  • gradle someTestTask --tests *UiTest someOtherTestTask --tests *WebTest*ui

たとえば my.test.HelloSpec クラスに定義されたテストを全て実行するためには、以下のコマンドを実行します:

gradle test --tests my.test.HelloSpec

まぁ、ただ個別にテストを実行するには IDE を使う方が楽ちん。 IDE 上で Spock を使う方法は後日に。 って、普通に JUnit のように使えるようですけど。

Maven 3

次は Maven3 上で Spock を使う方法。 試した Maven3 のバージョンは 3.2.1 です。 かなり久し振りに Maven 使った(というかインストールした)。 プロジェクト構造は Gradle と同じ(Gradle が Maven2/3 と同じという方が正しいかな?):

  • ${project.rootDir}
    • build.gradle
    • src
      • main
        • java
        • groovy
      • test
        • groovy
          • my
            • test
              • HelloSpockTest.groovy

Spock テストのファイルを HelloSpockTest.groovy に変更してるのに注意。 もちろんクラス名も変更してます。 どうも、Maven3 に(というか、テストを実行している Surefire プラグインに)テストであることを認識してもらうためにはクラス名が Test で終わらないといけないようです。 こちらにテストとして含めるファイル名(クラス名)の指定方法が書かれてますが、試してもうまくいかなかったのでスルーします。 Gradle の build.gradle に対応する pom.xml は以下のようにします(XML 助長過ぎる・・・):

<?xml version="1.0"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
         
  <modelVersion>4.0.0</modelVersion>
  <groupId>my.test</groupId>
  <artifactId>spock-test</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>jar</packaging>
  <name>Spock Test</name>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>
  
  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.1</version>
        <configuration>
          <compilerId>groovy-eclipse-compiler</compilerId>
          <source>1.7</source>
          <target>1.7</target>
          <!--verbose>true</verbose-->
        </configuration>

        <dependencies>
          <dependency>
            <groupId>org.codehaus.groovy</groupId>
            <artifactId>groovy-eclipse-compiler</artifactId>
            <version>2.8.0-01</version>
          </dependency>

          <dependency>
            <groupId>org.codehaus.groovy</groupId>
            <artifactId>groovy-eclipse-batch</artifactId>
            <version>2.1.8-01</version>
          </dependency>
        </dependencies>
      </plugin>

      <plugin>
        <groupId>org.codehaus.groovy</groupId>
        <artifactId>groovy-eclipse-compiler</artifactId>
        <version>2.8.0-01</version>
        <extensions>true</extensions>
      </plugin>
    </plugins>
  </build>

  <dependencies>
    <dependency>
      <groupId>org.codehaus.groovy</groupId>
      <artifactId>groovy-all</artifactId>
      <version>2.1.8</version>
    </dependency>

    <dependency>
      <groupId>org.spockframework</groupId>
      <artifactId>spock-core</artifactId>
      <version>0.7-groovy-2.0</version>
      <scope>test</scope>
    </dependency>    
  </dependencies>
</project>
  • いまのところ、動かせる Groovy のバージョンは 2.1.8 のようです。 groovy-eclipse-batch プラグインが対応していない Groovy のバージョンはダメなよう(試してないけど)。
  • 詳細な情報を出力したい場合は、maven-compiler-plugin の設定の箇所の、コメントアウトしてある verbose 要素の部分のコメントを外します。
  • java のソースコードのバージョンを指定する source/target に 1.8 を指定すると、いまのところ例外が投げられるようです。 ラムダ式とか使ってなくても。

pom.xml の XML 地獄以外は Gradle の場合と対して変わりませんね。 テストの実行は簡単。 プロジェクトのルートディレクトリで以下のコマンドを実行します:

mvn test

テストのフィルタリングもできるようですが、まぁ、いいでしょう。

コマンドライン

最後はコマンドラインから Spock を使う方法。 Spock wiki にはコマンドライン上で Jar ファイルをクラスパスに指定してテストを実行する方法が書かれてますが、ここでは Grape によって Jar ファイルを自動ダウンロードする方法を見てみましょう。 実質的には同じですが。 Grape によって Spock を使うには、Spock テストのコードに、以下のような @Grab, @GrabExclude アノテーションを追加します:

// HelloSpock.groovy
@Grab("org.spockframework:spock-core:0.7-groovy-2.0")
@GrabExclude("org.codehaus.groovy:groovy-all")
import spock.lang.*

class HelloSpock extends Specification{

    def "length of Spock's and his friends' names"(){
        expect:
        name.size == length

        where:
        name     | length
        "spock"  | 5
        "Kirk"   | 4
        "Scotty" | 6
    }
}

2つのアノテーション以外は同じです。 @GrabExclude アノテーションは、groovy-all.jar のバージョン衝突を防ぐために付けておく必要があります。 テストの実行は普通の Groovy スクリプトの実行と同じです:

groovy HelloSpock
  • コマンドを実行するディレクトリ上にある Groovy コードは自動でクラスパスに含められます。 それ以外に使用するソースコードがある場合は、そのファイルがあるディレクトリを「-cp」オプションによってクラスパスに含める必要があります。
  • Grape を使わないなら「-cp」オプションで Spock Framework と JUnit の Jar ファイルをクラスパスに追加します。 JUnit はバージョンによっては他に Jar が必要だったかもしれません。 そのあたりの依存性解決が面倒なので Grape でやっておく方が無難かと。

とりあえず Spock を使うための環境整備はこんなものですかね。 通常の開発では IDE からテストを行うことの方が多いと思いますが、Spock テストは JUnit が動く IDE 上では同じように使えるので便利です。 後は Gradle のマルチプロジェクトで使う場合をもうちょっと試したいところですが、それらは次回以降に見ていきたいと思います。

JUnit実践入門 ~体系的に学ぶユニットテストの技法 (WEB+DB PRESS plus)

JUnit実践入門 ~体系的に学ぶユニットテストの技法 (WEB+DB PRESS plus)

Jenkins実践入門 ?ビルド・テスト・デプロイを自動化する技術 (WEB+DB PRESS plus)

Jenkins実践入門 ?ビルド・テスト・デプロイを自動化する技術 (WEB+DB PRESS plus)

  • 作者: 佐藤聖規,和田貴久,河村雅人,米沢弘樹,山岸啓,川口耕介
  • 出版社/メーカー: 技術評論社
  • 発売日: 2011/11/11
  • メディア: 単行本(ソフトカバー)
  • 購入: 26人 クリック: 496回
  • この商品を含むブログ (65件) を見る
プログラミングGROOVY

プログラミングGROOVY

  • 作者: 関谷和愛,上原潤二,須江信洋,中野靖治
  • 出版社/メーカー: 技術評論社
  • 発売日: 2011/07/06
  • メディア: 単行本(ソフトカバー)
  • 購入: 6人 クリック: 392回
  • この商品を含むブログ (155件) を見る

*1:これを testCompile にするとテストにのみ Groovy を使うようにできるのかな? とりあえずテストは機能しますがパッケージングしたりするのが面倒なので成果物が Groovy に依存しないかどうかは試してません。

*2:もしくは、一度「gradle wrapper」と実行して、その後に「gradlew test」と 'w' を付けて実行。