LoginSignup
3
0

More than 1 year has passed since last update.

Spockについてまとめてみた

Posted at

Spockとは

Spockは、Groovy言語で記述されたBDDスタイルのテストフレームワーク。
JUnit4を基盤としていますが、より高水準のテスト記述が可能で、可読性の高いテストコードの記述が可能。

Spockの特徴

BDDスタイルのテストコードの記述が容易

Spockは、BDDスタイルの記述が容易であり、given-when-thenの記述に基づいて、テストケースを自然な文書化された形式で記述できる。
また、JUnitに比べてテストコードが簡潔で、可読性が高くなるというメリットがある。

データ駆動テストのサポート

Spockは、データ駆動テストをサポートしている。whereブロックを使用して、入力データを定義することができる。
また、@Unrollアノテーションを使用することで、入力データに対応するテストケースのラベルを生成することができる。

モックオブジェクトの簡単な作成

Spockは、Groovy言語に組み込まれているメタプログラミングの機能を使用して、モックオブジェクトを簡単に作成することができる。
また、Interaction Based Testingのような形式で、オブジェクト間の相互作用をテストすることができる。

その他特徴

  • 構文がシンプルで読みやすい
  • テスト実行速度が速く、JUnitよりも高速
  • テストコードを簡潔に書くことができる
  • Groovy言語により、Javaよりも簡単に書くことができる

Spockのコーディング例

HTTPリクエストのテスト

class MySpec extends Specification {

    def "test GET request to /hello returns 'Hello World!'"() {
        given:
        def url = "http://localhost:8080/hello"

        when:
        def response = new URL(url).text

        then:
        response == "Hello World!"
    }

    def "test POST request to /hello with 'John' in request body returns 'Hello John!'"() {
        given:
        def url = "http://localhost:8080/hello"
        def requestBody = ["name": "John"]

        when:
        def response = RestAssured.given()
                      .contentType(ContentType.JSON)
                      .body(requestBody)
                      .post(url)
                      .andReturn()
                      .response
                      .asString()

        then:
        response == "Hello John!"
    }
}

上記の例は、Spockを使用してHTTPリクエストのテスト。
1つ目のテストケースでは、URLにGETリクエストを送信し、レスポンスが"Hello World!"であることをアサートしている。
2つ目のテストケースでは、POSTリクエストを送信して、リクエストボディに"name": "John"を含めている

class SampleSpockSpec extends spock.lang.Specification {
    def "1 + 1 は 2 であること"() {
        expect:
        1 + 1 == 2
    }
}

Given-When-Then構文

Spockでは、BDDのGiven-When-Then構文に対応しており、テストコードの可読性が向上する。
以下は、SpockでGiven-When-Then構文を使用したテストコードの例。

class SampleSpockSpec extends spock.lang.Specification {
    def "入力された値が2である場合、処理結果は4であること"() {
        given:
        def inputValue = 2

        when:
        def result = inputValue * 2

        then:
        result == 4
    }
}

Mockオブジェクトの利用

Spockでは、Mockオブジェクトを利用することができる。
以下は、SpockでMockオブジェクトを利用したテストコードの例。

class SampleSpockSpec extends spock.lang.Specification {
    def "SampleServiceの処理結果が正しく返却されること"() {
        given:
        def sampleService = Mock(SampleService)
        sampleService.execute() >> "success"

        when:
        def result = sampleService.execute()

        then:
        result == "success"
    }
}

例外処理のテスト

以下は、Spockで例外処理をテストするテストコードの例。

class SampleSpockSpec extends spock.lang.Specification {
    def "数値が正しくない場合、例外が発生すること"() {
        given:
        def inputValue = "hoge"

        when:
        def result = { Integer.parseInt(inputValue) }

        then:
        def thrownException = thrown(NumberFormatException)
    }
}

詳細なコーディング例1: データベース接続のテスト

データベース接続を行うクラスをテストする例です。データベース接続が正常に行われているか、例外が発生するかどうかをテストする。

import spock.lang.*
import groovy.sql.Sql

class DatabaseConnectionSpec extends Specification {
    def "正常なデータベース接続が行えること"() {
        given:
        def sql = Sql.newInstance("jdbc:mysql://localhost/test", "user", "password", "com.mysql.jdbc.Driver")
        
        when:
        def result = sql.execute("SELECT COUNT(*) FROM users")
        
        then:
        result.rows[0][0] == 3
    }
    
    def "異常なデータベース接続時に例外が発生すること"() {
        given:
        def sql = Sql.newInstance("jdbc:mysql://localhost/unknown", "user", "password", "com.mysql.jdbc.Driver")
        
        when:
        def exception = thrown(Exception)
        def result = sql.execute("SELECT COUNT(*) FROM users")
        
        then:
        exception.message == "Unknown database 'unknown'"
    }
}
  • givenブロックでデータベース接続に必要な情報を準備。
  • whenブロックでSQLクエリを実行し、結果を取得。
  • thenブロックで期待する結果をアサート。
  • 2つ目のテストケースでは、期待する例外が発生することをアサートするために、thrownメソッドを使用。

詳細なコーディング例2: REST APIのテスト

REST APIをテストする例です。HTTPリクエストを送信し、レスポンスをアサートする。

import spock.lang.*
import io.restassured.RestAssured
import io.restassured.http.ContentType

class RestApiSpec extends Specification {
    def "GETリクエストが成功すること"() {
        when:
        def response = RestAssured.get("https://jsonplaceholder.typicode.com/posts/1")
        
        then:
        response.statusCode == 200
        response.contentType == ContentType.JSON
        response.body.title == "sunt aut facere repellat provident occaecati excepturi optio reprehenderit"
    }
    
    def "POSTリクエストが成功すること"() {
        given:
        def body = ["title": "foo", "body": "bar", "userId": 1]
        
        when:
        def response = RestAssured.given()
            .contentType(ContentType.JSON)
            .body(body)
            .post("https://jsonplaceholder.typicode.com/posts")
        
        then:
        response.statusCode == 201
        response.contentType == ContentType.JSON
        response.body.title == "foo"
        response.body.body == "bar"
    }
}
  • whenブロックでHTTPリクエストを送信し、レスポンスを取得。
  • thenブロックでレスポンスの結果チェックを行う。

その他コーディング例1: テスト対象クラスのprivateメソッドをテストする

class Foo {
    private int add(int a, int b) {
        return a + b
    }
}

class FooSpec extends spock.lang.Specification {
    def "test private method add"() {
        given:
        def foo = new Foo()

        expect:
        foo."$spock_private_method"(a, b) == result

        where:
        a    | b    | result
        1    | 2    | 3
        10   | 20   | 30
        100  | 200  | 300
    }
}

上記の例では、テスト対象の Foo クラス内に存在する add メソッドをテストしている。
Spockではprivateメソッドも直接テストすることができます。テストケースのデータパラメータ化には、whereブロックを使用している。

その他コーディング例2: 引数の異なる複数のテストケースを一括でテストする

class MathUtil {
    static int multiply(int a, int b) {
        return a * b
    }
    static int add(int a, int b) {
        return a + b
    }
}

class MathUtilSpec extends spock.lang.Specification {
    def "test multiply"() {
        expect:
        MathUtil.multiply(a, b) == result

        where:
        a | b | result
        1 | 2 | 2
        5 | 5 | 25
    }

    def "test add"() {
        expect:
        MathUtil.add(a, b) == result

        where:
        a | b | result
        1 | 2 | 3
        5 | 5 | 10
    }
}

上記の例では、 MathUtil クラスの multiply メソッドと add メソッドを、複数の異なる引数でテストしています。 where ブロックを使うことで、異なる入力値と期待値をまとめて定義し、一括でテストすることができる。

参考記述

【Groovy × Spock】Spockでテストを書いてみよう
Spockの基本
JUnit代わりにSpockを使ってみる
Spockを使ってJavaのテストを効率化する

3
0
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
3
0