新機能と主な変更点

0.7

スナップショットリポジトリの移動

Spockのスナップショットは http://oss.sonatype.org/content/repositories/snapshots/ から取得できるようになりました。

新しいリファレンスドキュメント

Spockの新しいリファレンスドキュメントはhttp://docs.spockframework.orgで参照できます。ここにはhttp://wiki.spockframework.orgにあるドキュメントを徐々に移動していきます。このドキュメントは各バージョン毎に公開しています(例えばhttp://docs.spockframework.org/en/spock-0.7-groovy-1.8)。スナップショットの最新ドキュメントはhttp://docs.spockframework.org/en/latestを参照してください。Spock 0.7の時点ではデータ駆動テスト相互作用中心のテストの記述が完了しています。

TooManyInvocationsErrorのモッキング失敗メッセージの改善

TooManyInvocationsError発生時の診断メッセージが大幅に改善されました。以下に例を示します。

Too many invocations for:

3 * person.sing(_)   (4 invocations)

Matching invocations (ordered by last occurrence):

2 * person.sing("do")   <-- this triggered the error
1 * person.sing("re")
1 * person.sing("mi")

リファレンスドキュメント

TooFewInvocationsErrorのモッキング失敗メッセージの改善

TooFewInvocationsError発生時の診断メッセージが大幅に改善されました。以下に例を示します。

Too few invocations for:

1 * person.sing("fa")   (0 invocations)

Unmatched invocations (ordered by similarity):

1 * person.sing("re")
1 * person.say("fa")
1 * person2.shout("mi")

リファレンスドキュメント

スタブ

従来のモックオブジェクトに加えて、スタブのサポートを追加しました。

def person = Stub(Person)

スタブはモックオブジェクトの機能制限版で、モックに比べより本物の値に近い値を返します。また、スタブは多重度を定義できない以外は、モックのインタラクションと同じよう見えますが、読み手に対し役割をより明確にできる効果があります。

リファレンスドキュメント

スパイ

従来のモックオブジェクトに加えて、スパイのサポートを追加しました。

def person = Spy(Person, constructorArgs: ["Fred"])

スパイは、この例のPersonインスタンスのように本物のオブジェクト上に構築されます。インタラクションが一致しないスパイに対するすべての呼び出しは、本物のオブジェクトへと処理が委譲されます。このようにスパイを使用することで、本物のオブジェクトの必要な部分のみ振る舞いを変更し、またその呼び出しを監視することができます。さらに、スパイはパーチャルモックとして使用することも可能です。

リファレンスドキュメント

モック作成時のインタラクション定義

モック作成時にインタラクションを定義できようになりました。

def person = Mock(Person) {
    sing() >> "tra-la-la
    3 * eat()
}

この機能は、特にStubsで便利な機能です。

リファレンスドキュメント

Groovyモック

Groovyのコードに特化した特別なモックオブジェクトの機能を追加しました。

def mock = GroovyMock(Person)
def stub = GroovyStub(Person)
def spy = GroovySpy(Person)

Groovyモックは、自動的にgroovy.lang.GroovyObjectを実装します。これは静的に定義されたメソッドと同じように、動的なメソッドに対するスタビングとモッキングが可能です。Groovyモックは、GroovyのコードではなくJavaから呼び出されると、通常のモックと同じように振舞います。

リファレンスドキュメント

グローバルモック

Groovyモックはグローバルに作成できます。

GroovySpy(Person, global: true)

このグローバルモックはクラスの型にのみ使用できます。これは、その型の全てのインスタンスを差し替え、スタビングとモッキングはこれらの型全てに影響を与えます(このような動作をするGroovyのMockForStubForをご存知かも知れません)。さらにグローバルモックは、その型のコンストラクタ、およびstaticメソッドのモッキングが可能です。

リファレンスドキュメント

コンディションのグループ化

この機能はGroovyのObject.withからインスパイアされました。これはSpecification.withメソッドを使用することで、特定の対象オブジェクトに対するコンディションのグループ化が行えます。

def person = new Person(name: "Fred", age: 33, sex: "male")

expect:
with(person) {
    name == "Fred"
    age == 33
    sex == "male"
}

インタラクションのグループ化

withメソッドはインタラクションのグループ化にも使用できます。

def service = Mock(Service)
app.service = service

when:
app.run()

then:
with(service) {
    1 * start()
    1 * act()
    1 * stop()
}

リファレンスドキュメント

ポーリングコンディション

AsyncConditionsBlockingVariable(s)を統合した、非同期のコードをテストするためのユーティリティを追加しました。

def person = new Person(name: "Fred", age: 22)
def conditions = new PollingConditions(timeout: 10)

when:
Thread.start {
    sleep(1000)
    person.age = 42
    sleep(5000)
    person.name = "Barney"
}

then:
conditions.within(2) {
    assert person.age == 42
}

conditions.eventually {
    assert person.name == "Barney"
}

Eclipse用の実験的なDSLサポート

Groovy EclipseがSpockのDSLを理解しやすいように、Groovy EclipseためのDSLディスクリプタを同封するようにしました。このディスクリプタは、IDEによって自動的に検出され有効になります。以下に例を示します。

// currently need to type variable for the following to work
Person person = new Person(name: "Fred", age: 42)

expect:
with(person) {
    name == "Fred" // editor understands and auto-completes 'name'
    age == 42      // editor understands and auto-completes 'age'
}

他の例では以下のようになります。

def person = Stub(Person) {
    getName() >> "Fred" // editor understands and auto-completes 'getName()'
    getAge() >> 42      // editor understands and auto-completes 'getAge()'
}

DSLのサポートは、Groovy Eclipse 2.7.1以降で使用できます。これは、Groovy Eclipseの設定で無効にすることもできます。

IntelliJ IDEA用の実験的なDSLサポート

Intellij IDEAがSpockのDSLを理解しやすいように、Intellij IDEAためのDSLディスクリプタを同封するようにしました。このディスクリプタは、IDEによって自動的に検出され有効になります。以下に例を示します。

def person = new Person(name: "Fred", age: 42)

expect:
with(person) {
    name == "Fred" // editor understands and auto-completes 'name'
    age == 42      // editor understands and auto-completes 'age'
}

他の例では以下のようになります。

def person = Stub(Person) {
    getName() >> "Fred" // editor understands and auto-completes 'getName()'
    getAge() >> 42      // editor understands and auto-completes 'getAge()'
}

DSLのサポートはIntelliJ IDEA 11.1以降で使用できます。

Specificationクラスの分割

spock.lang.Specificationクラスの一部を、2つのスーパークラスに引き上げました。ひとつはspock.lang.MockingApiで、これにはモックに関連する全てのメソッドが含まれます。そして、org.spockframework.lang.SpecInternalsには、直接使用することを意図していない内部メソッドが含まれます。

notThrownnoExceptionThrownの失敗メッセージの改善

単に例外を通知する代わりに、Specification.notThrownSpecification.noExceptionThrownは、以下のような失敗メッセージを表示するようになりました。

Expected no exception to be thrown, but got 'java.io.FileNotFoundException'

Caused by: java.io.FileNotFoundException: ...

HamcrestSupport.expect

spock.util.matcher.HamcrestSupportクラスに新たなexpectメソッドを追加しました。これを使用することで、thenブロック内のHamcrestのアサーションが、より読みやすくなります。

when:
def x = computeValue()

then:
expect x, closeTo(42, 0.01)

@Beta

最近追加されたクラスや、メソッドには@Betaアノテーションが付与されている場合があります。これは今後、互換性のない変更が行われる可能性があることを表しています。さらにこれが、ユーザーからの貴重なフィードバックを得る機会になることも期待しています(あなたのフィードバックをお待ちしています!)。多くの場合、@Betaは1回、または2回のリリース内で削除されます。

修正済みの課題

修正された問題の一覧は、課題管理システムを参照してください。

0.6

モッキングの改善

モックフレームワークでは、いくつかのケースでよりわかりやすい診断メッセージを提供するようにしました。

また、複数のレスポンスの定義をチェーンできるようになっています。以下は、最初のbarメソッドの呼び出しにIOExceptionをスローし、 次の呼び出しでは数字の1、2、3を返し、それ以降の呼び出しにはRuntimeExceptionをスローします。

foo.bar() >> { throw new IOException() } >>> [1, 2, 3] >> { throw new RuntimeException() }

foo.bar(*_)のように、任意の引数リスト(空のリストを含む)と、一致させることが可能になりました。

また、Hamcrestを使用して、引数制約を指定できるようになっています。

import static spock.util.matcher.HamcrestMatchers.closeTo

...

1 * foo.bar(closeTo(42, 0.001))

JUnitルールサポートの拡張

org.junit.rules.MethodRule(これはJUnit 4.9でdeprecatedになりました)を実装したルールに加え、org.junit.rules.TestRuleインタフェースを実装したルールのサポートを追加しました。また、JUnitの新たな@ClassRuleアノテーションのサポートも追加しています。さらにルールの定義を自動的に認識し、明示的に初期化しなくても動作するようになっています。これはデフォルトコンストラクタを使用し、Spockが自動的にルールを初期化します。@Unrollアノテーションを使用したネーミングパターンは、@TestNameルールや、その他一般的なルールにも適用されるようになりました。

SpockのTestRuleサポートに関する 課題 240 の制約を確認してください。

コンディションの表示の改善

2つのオブジェクトを==演算で比較すると、これらは等しないにもかかわらず、文字列表現はまったく同じものになってしまいます。このような場合に、オブジェクトの型を出力するようにしました。

enteredNumber == 42
|             |
|             false
42 (java.lang.String)

JUnitのフィクスチャアノテーション

従来のSpcokのフィクスチャメソッドに加え(または代わりに)、JUnitの@Before@After@BeforeClassそして@AfterClassを使用してフィクスチャメソッドが定義できるようになりました。

Tapestry 5.3のサポート

Howard Lewis Shipからのコントリビュートにより、TapestryモジュールがTapestry 5.3 互換となりました。従来の5.x以前のバージョンも、まだサポートされています。

IBM JDKのサポート

IBMのJDKで検証を行いバグを回避をすることで、IBMのJDKで問題なく動作するようになりました。

JUnit互換の改善

org.junit.internal.AssumptionViolatedExceptionのハンドリングを追加しました。これにより、JUnitのAssume関連のAPIが問題なく動作するようになります。また、@Unrollを付与したメソッドが、IDE上で警告とならないようになりました。

@Unrollの改善

@Unrollのネーミングパターンがアノテーションの引数だけでなく、メソッド名で指定できるようになりました。

@Unroll
def "maximum of #a and #b is #c"() {
    expect:
    Math.max(a, b) == c

    where:
    a | b | c
    1 | 2 | 2
}

また、ネーミングパターンでプロパティアクセス、引数なしのメソッド呼び出しが使用できるようになっています。

@Unroll
def "#person.name.toUpperCase() is #person.age years old"() { ... }

さらに、@Unrollアノテーションが、スペッククラスにも適用できるようにしました。この場合は、すべてのデータ駆動のフィーチャメソッドが展開実行されます。

@Timeoutの改善

@Timeoutアノテーションが、スペッククラスにも適用にも適用できるようになりました。この場合は、@Timeoutがすでに設定されているフィーチャメソッド除き、すべてのフィーチャメソッドにタイムアウトが適用されます。タイムアウトが付与されたメソッドは、通常のテストフレームワークのスレッドで実行されます。これは、スレッドローカルの状態に依存しているテストを行う場合に重要になることがあります(たとえばGrailsのインテグレーションテスト)。また、タイムアウトを強制することができるチャンスをより多くするために、スレッド中断の動作が改善されました。

タイムアウトが発生した場合にスローされる例外の中に、テスト実行のスタックトレースが含まれるようになっています。これにより、どのスタックでタイムアウトになったのか、簡単に把握できるようになります。

データテーブルのシンタックス改善

テーブルのセルを論理和の記号(||)で、区切ることができるようになりました。これは、入力値と出力の期待値を、視覚的に区別するために使用できます。

...
where:
a | b || sum
1 | 2 || 3
3 | 1 || 4

Groovy 1.8/2.0のサポート

Spock 0.6ではGroovy 1.7、1.8、そして2.0それぞれに対応した、3つのバージョンを提供しています。これらの中から正しいバージョンを使用するようにしてください。例えば、Groovy 1.8を使用している場合は、spock-core-0.6-groovy-1.8を使用する必要があります(他のモジュールも同様です)。Groovy 2.0のバージョンはGroovyの2.0-beta-3-SNAPSHOTをベースにしており、http://m2repo.spockframework.orgからのみ利用可能です。Groovy 1.7と1.8のバージョンはMavenのセントラルリポジトリからも利用可能です。また次のバージョンからは、Groovy 1.7をサポートしない予定です。

Grails 2.0のサポート

SpockのGrailsプラグインを別のプロジェクトにしました。http://github.spockframework.org/spock-grailsで管理しています。このプラグインは、Grailsの1.3と2.0の両方をサポートしています。

Spock Grails pluginはGrails 2.0の新たなテストミックスインをすべてサポートしています。これにより既存のユニットテスト関連のクラスが非推奨になります(例えばUnitSpec)。ただしインテグレーションテストのために、IntegrationSpecは引き続き使用する必要があります。

Intellij IDEA連携

JetBrains の開発メンバーにより、データテーブルの周りのいくつか便利な機能が追加されました。まず、コードを再フォーマットすると、データテーブルが自動的にレイアウトされます。データ変数はこれまでのように”unknown”とは表示されません。さらに、これらの型はテーブルの値から型が推論されます(なんと!)。

Githubリポジトリ

すべてのソースコードをhttp://github.spockframework.org/へ移動しました。ここにはGrails Spock pluginSpock Exampleプロジェクト、そしてSpock Web ConsoleがGithubのプロジェクトとして置いてあります。また、各種プレゼンテーション用の、スライドやサンプルコードも参照可能になっています(例えばこれ)。

Gradleビルド

SpockをGradleで構築するようにしました。Spockを自分自身でビルドする場合は、GitHub repoをクローンし、gradlew buildを実行することで簡単にビルドできます。事前にビルドツールをインストールしておく必要はありません。Spockをビルドするために必要な前準備は、JDKのインストール(1.5以上)のみです。

修正済みの課題

修正された問題の一覧は、課題管理システムを参照してください。