LoginSignup
6
1

More than 3 years have passed since last update.

Spockを始めるのに最低限必要なGroovyの基本知識

Posted at

はじめに

「プロダクトコードはJavaだけど、Spockを使ってテストを書いてみたい...だけどGroovyを習得するのが大変そう」

と思っている方がいるかもしれません(筆者がそうでした)。たしかにGroovy自体は奥の深い言語なのですが、とりあえずSpockでテストを書く分には基本的なところだけ押さえておけば不自由しません。

というわけで、実際にJavaのプロジェクトにSpockを導入してみて、まずはこれだけ押さえておけばSpockを始められる、という基本をまとめてみました。

基本のキ

アサート

この後のサンプルソースで出てくるので先に説明します。

  • 必ず真になるべき条件の表明はassert 真と評価されるべき式で記述します。
  • Groovyでは文末のセミコロン;は省略できます。
assert 1 < 2

クラスの定義

  • Javaと同じくextendsでクラス継承、implementsでインタフェース継承です。
  • アクセス修飾子はデフォルトがpublicです。Groovyの中ではpublicもprivateも差がないので、基本アクセス修飾子は書かなくてよいです。
class SomeSpec extends Specification {
}

メソッドの定義

  • Javaと同じように書くこともできますし、引数・戻り値の型名を省略して書くことも可能です。
  • 戻り値の型名を省略する場合はdefキーワードが必須です。
  • メソッドで最後に評価された式が暗黙的に返されるため、途中のリターンでなければreturnを省略できます。
  • アクセス修飾子はデフォルトがpublicです。Groovyの中ではpublicもprivateも差がないので、基本アクセス修飾子は書かなくてよいです。
// 型名を明示
int increment(int arg) {
    return arg + 1
}

// 型名を省略
def increment2(arg) {
    arg + 1
}

インスタンス生成、変数への代入

  • インスタンスの生成はJavaと同じくnew演算子を使います。
  • 変数はdefで定義します。
  • 型推論が働くので左辺に型名は不要です(明示することも可能です)。
def pet = new Pet()
Pet pet2 = new Pet() //型を明示するならdefは不要

インスタンス変数の定義(プロパティ)

  • defまたは型名指定でインスタンス変数を定義します。
  • 自動でgetter/setterが生成されます。
class Pet {
    String name
    def age
}

プロパティアクセス

  • 自動生成されたgetter/setterを明示的に利用することができます。
  • インスタンス変数へのアクセスはgetter/setterを使ったアクセスに変換されます。
def pet = new Pet()

// 明示的なアクセサの利用
pet.setName("Pochi")
assert pet.getName() == "Pochi"

// 暗黙的なアクセサの利用
pet.age = 2
assert pet.age == 2

これはJavaオブジェクトに対するアクセスにも適用されます。以下のJavaクラスがあったとして、

// Hoge.java
public class Hoge {
    private String name;
    public String getName() {
        return name + "!";
    }
    public void setName(String name) {
        //大文字変換
        this.name = name.toUpperCase();
    }
}

変数へのアクセスが実際にはgetter/setter経由のプロパティアクセスとなっていることがわかります。

def hoge = new Hoge()
hoge.name = "fuga" // 実はhoge.setName
assert hoge.name == "FUGA!" // 実はhoge.getName

等値性と同一性

  • Groovyの==はJavaのequalsであり、値として等値であるかを評価します。
  • 参照の同一性はisメソッドを使って評価します。
  • アプリケーションの大半のケースでは等値性を問うので、プリミティブ型、参照型に関わらず==を使って比較すればOKです。
def date1 = LocalDate.of(2020, 1, 1)
def date2 = LocalDate.of(2020, 1, 1)

assert date1 == date2 // 等値性
assert !date1.is(date2) // 同一性

Truthy

  • boolean型以外の方も(JavaScriptのような)真偽値判定ができます(if文の条件にも使えます)。
assert 1 // 数値型:非ゼロは真
assert !0 // 数値型:ゼロは偽

assert "hoge" // 文字列型:非空文字列は真
assert !"" // 文字列型:空文字列は偽

assert new Pet() // オブジェクト型:非nullは真
def petNull
assert !petNull // オブジェクト型:nullは偽

コレクション

リテラル表記

  • リストは[]の中にカンマ区切りで要素を列挙します。
def emptyList = []
def list = [1, 2, 3]
  • マップのリテラルはリストと似ていますが、:でキーと値を区切って列挙します。
  • 文字列をキーとする場合、""で囲わなくてもOKです。
  • enumの要素をキーに使う場合は括弧()で囲う必要があります。
def emptyMap = [:]
def map1 = ["one": 1, "two": 2]
def map2 = [one:1, two: 2]
assert map1 == map2
def map3 = [(BloodTypes.A):"A型"]
  • セットはリストと同じリテラル表記で、後ろにas Setを付けます。
def emptySet = [] as Set
def set = [1, 2, 3] as Set

要素へのアクセス

  • リストの要素は[添字]でアクセスできます。
  • マップの要素は[キー]でアクセスできます。
assert list[0] == 1
assert map2["one"] == 1

Truthy

いずれのコレクション型も真偽判定に使えます。

assert list // コレクション型:非emptyなら真
assert !emptyList // コレクション型:emptyなら偽

クロージャ

クロージャはJavaでいうラムダ式にあたるものです。
Spockでクロージャを使うのは、Mockを使う場合やテスト対象のJavaメソッドが関数型インタフェースを引数に要求するときなど限られた場面だと思うので、この章は必要になる時まで飛ばしてもらっても大丈夫です。

引数が1個

  • { 引数 -> 文 } という形式でクロージャを定義します。
  • 引数が1つの場合は省略可能で、その場合はitという引数が暗黙的に定義されます。
def doubler = { x -> x * 2 }
assert doubler(1) == 2
// 暗黙のitを使用すれば以下のように書いても同じ
def doubler2 = { it * 2 }
assert doubler2(1) == 2

引数が0個

  • 引数がないクロージャの見た目は{}で囲われたコードブロックです。
  • GroovyのクロージャはJavaのラムダ式と違い、外側の変数を変更できます
def num = 0
def counter = { num++ }
counter()
assert num == 1

引数が複数個

  • 引数が複数の場合、括弧で囲まずに引数リストを列挙します。
def adder = { x, y -> x + y }
assert adder(1, 2)

複数の文からなるクロージャ

  • クロージャのブロック内で改行して複数の文を記述できます。
  • 1行で書きたければ;で文を区切ることも可能です。
def doubleThenAddOne = {
    it = it * 2
    it + 1
}
assert doubleThenAddOne(2) == 5

def doubleThenAddOne2 = { it = it * 2; it + 1 }
assert doubleThenAddOne2(2) == 5

Spockのテストケースを書く

ここまで学んだGroovyの基礎知識があれば、基本的なSpockのテストケースを書くことができます。

// spock.lang.Specification を継承してテストクラスを作成
class DataConverterSpec extends Specification {

    // テストメソッドの定義
    def "正しく変換できる"() {
        given:
        // リテラル表記でListを作成してdefで変数へ代入
        def source = [1, 2, 3]
        def sut = new DataConverter(source)

        when:
        // Javaの関数インターフェースにはクロージャを渡すことができる
        sut.apply({ it * 10 })
        def data = sut.getData()

        then:
        // == による等値比較。Listの等値比較も簡単
        data == [10, 20, 30]
    }
}

おわりに

Spockによるテスティングを始める上で必要なGroovyの基本知識をまとめました。

実際に筆者がSpockを始めてからよく調べていたことを盛り込んだつもりですが、不足や誤り、わかりくい点などあればフィードバックを頂けると幸いです。

6
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
6
1