1. 概要

このチュートリアルでは、Spock拡張機能を見ていきます。

場合によっては、仕様のライフサイクルを変更または強化する必要があります。 たとえば、条件付き実行を追加したり、ランダムに失敗した統合テストを再試行したりします。 このために、Spockの拡張メカニズムを使用できます。

Spockには、仕様のライフサイクルにフックできるさまざまな拡張機能があります。

最も一般的な拡張機能の使用方法を見つけましょう。

2. Mavenの依存関係

始める前に、Maven依存関係を設定しましょう。

<dependency>
    <groupId>org.spockframework</groupId>
    <artifactId>spock-core</artifactId>z
    <version>1.3-groovy-2.4</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.codehaus.groovy</groupId>
    <artifactId>groovy-all</artifactId>
    <version>2.4.7</version>
    <scope>test</scope>
</dependency>

3. 注釈ベースの拡張機能

Spockの組み込み拡張機能のほとんどは、注釈に基づいています。

スペッククラスまたは機能にアノテーションを追加して、特定の動作をトリガーできます。

3.1. @Ignore

一部の機能メソッドまたはスペッククラスを無視する必要がある場合があります。同様に、変更をできるだけ早くマージする必要があるかもしれませんが、継続的インテグレーションは失敗します。 一部の仕様を無視しても、マージを成功させることができます。

メソッドレベルで@Ignoreを使用して、単一の指定メソッドをスキップできます。

@Ignore
def "I won't be executed"() {
    expect:
    true
}

Spockはこのテストメソッドを実行しません。そしてほとんどのIDEはテストをスキップとしてマークします。

さらに、クラスレベルで@Ignoreを使用できます。

@Ignore
class IgnoreTest extends Specification

テストスイートまたはメソッドが無視される理由を提供するだけです。

@Ignore("probably no longer needed")

3.2. @IgnoreRest

同様に、 @IgnoreRest アノテーションでマークできる1つを除いて、すべての仕様を無視できます。

def "I won't run"() { }

@IgnoreRest
def 'I will run'() { }

def "I won't run too"() { }

3.3. @IgnoreIf

場合によっては、条件付きで1つか2つのテストを無視したいことがあります。 その場合、は@IgnoreIfを使用できます。これは、引数として述語を受け入れます。

@IgnoreIf({System.getProperty("os.name").contains("windows")})
def "I won't run on windows"() { }

Spockは、述語の読み取りと書き込みを容易にするために、一連のプロパティとヘルパークラスを提供します。

  • os –オペレーティングシステムに関する情報( spock.util.environment.OperatingSystem を参照)。
  • jvm – JVMの情報( spock.util.environment.Jvm を参照)。
  • sys –マップ内のシステムのプロパティ。
  • env –マップ内の環境変数。

os プロパティを使用する間、前の例を書き直すことができます。 実際には、これは spock.util.environment.OperatingSystem クラスであり、たとえば isWindows()のようないくつかの便利なメソッドがあります。

@IgnoreIf({ os.isWindows() })
def "I'm using Spock helper classes to run only on windows"() {}

SpockSystem.getProperty(…)underhoodを使用することに注意してください。 主な目標は、複雑なルールや条件を定義するのではなく、明確なインターフェイスを提供することです。

また、前の例のように、クラスレベルで@IgnoreIfアノテーションを適用できます。

3.4. @Requires

場合によっては、述語論理をから反転する方が簡単です。 @IgnoreIf。 その場合、 @必要

@Requires({ System.getProperty("os.name").contains("windows") })
def "I will run only on Windows"()

したがって、 @Requiresは、OSがWindows の場合にのみこのテストを実行しますが、 @IgnoreIf、は、同じ述語を使用して、OSがの場合にのみテストを実行します。 notWindows。

一般的に、 無視されるときよりも、どの条件でテストが実行されるかを言う方がはるかに良いです

3.5. @PendingFeature

TDDでは、最初にテストを記述します。 次に、これらのテストに合格するためのコードを作成する必要があります。 場合によっては、機能を実装する前にテストをコミットする必要があります。

これは、 @PendingFeature:の良い使用例です。

@PendingFeature
def 'test for not implemented yet feature. Maybe in the future it will pass'()

@Ignore@PendingFeatureには主な違いが1つあります。 @PedingFeatureでは、テストが実行されますが、失敗は無視されます。

@PendingFeature でマークされたテストがエラーなしで終了した場合、アノテーションの削除を通知するために失敗として報告されます。

このようにして、最初は実装されていない機能の失敗を無視できますが、将来的には、これらの仕様は永久に無視されるのではなく、通常のテストの一部になります。

3.6. @Stepwise

@Stepwise アノテーションを使用して、仕様のメソッドを指定された順序で実行できます。

def 'I will run as first'() { }

def 'I will run as second'() { }

一般に、テストは決定論的である必要があります。一方が他方に依存してはなりません。 そのため、@Stepwiseアノテーションの使用は避けてください。

ただし、必要な場合は、@Stepwiseが@Ignore、@ IgnoreRest、または @IgnoreIfの動作をオーバーライドしないことに注意する必要があります。 これらのアノテーションを@Stepwiseと組み合わせる場合は注意が必要です。

3.7. @Timeout

仕様の単一メソッドの実行時間を制限して、より早く失敗する可能性があります。

@Timeout(1)
def 'I have one second to finish'() { }

これは、フィクスチャメソッドで費やされた時間をカウントするのではなく、1回の反復のタイムアウトであることに注意してください。

デフォルトでは、spock.lang.Timeoutは秒を基本時間単位として使用します。 ただし、他の時間単位を指定できます:

@Timeout(value = 200, unit = TimeUnit.SECONDS)
def 'I will fail after 200 millis'() { }

クラスレベルの@Timeoutは、すべての機能メソッドに個別に適用するのと同じ効果があります。

@Timeout(5)
class ExampleTest extends Specification {

    @Timeout(1)
    def 'I have one second to finish'() {

    }

    def 'I will have 5 seconds timeout'() {}
}

単一仕様のメソッドで@Timeoutを使用すると、常にクラスレベルが上書きされます。

3.8. @再試行

場合によっては、非決定論的な統合テストを行うことができます。 これらは、非同期処理などの理由で、または他の HTTP クライアントの応答に応じて、一部の実行で失敗する場合があります。 さらに、ビルドとCIを備えたリモートサーバーは失敗し、テストを実行して再度ビルドする必要があります。

この状況を回避するために、メソッドまたはクラスレベルで@Retryアノテーションを使用して、失敗したテストを繰り返すことができます

@Retry
def 'I will retry three times'() { }

デフォルトでは、3回再試行します。

テストを再試行する必要がある条件を判別することは非常に役立ちます。 例外のリストを指定できます。

@Retry(exceptions = [RuntimeException])
def 'I will retry only on RuntimeException'() { }

または、特定の例外メッセージがある場合:

@Retry(condition = { failure.message.contains('error') })
def 'I will retry with a specific message'() { }

非常に便利なのは、遅延を伴う再試行です。

@Retry(delay = 1000)
def 'I will retry after 1000 millis'() { }

そして最後に、ほとんどの場合と同様に、クラスレベルで再試行を指定できます。

@Retry
class RetryTest extends Specification

3.9. @RestoreSystemProperties

@RestoreSystemPropertiesを使用して環境変数を操作できます。

この注釈を適用すると、変数の現在の状態が保存され、後で復元されます。 また、setupまたはcleanupメソッドも含まれています。

@RestoreSystemProperties
def 'all environment variables will be saved before execution and restored after tests'() {
    given:
    System.setProperty('os.name', 'Mac OS')
}

その点に注意してくださいシステムプロパティを操作しているときに、テストを同時に実行しないでください。 私たちのテストは非決定論的かもしれません。

3.10. 人間に優しいタイトル

@Title アノテーションを使用して、人間にわかりやすいテストタイトルを追加できます。

@Title("This title is easy to read for humans")
class CustomTitleTest extends Specification

同様に、 @Narrative アノテーションと、複数行の Groovy S tringを使用して、仕様の説明を追加できます。

@Narrative("""
    as a user
    i want to save favourite items 
    and then get the list of them
""")
class NarrativeDescriptionTest extends Specification

3.11. @See

1つ以上の外部参照をリンクするには、@Seeアノテーションを使用できます。

@See("https://example.org")
def 'Look at the reference'()

複数のリンクを渡すために、Groovy []オペランドを使用してリストを作成できます。

@See(["https://example.org/first", "https://example.org/first"])
def 'Look at the references'()

3.12. @Issue

機能メソッドが1つまたは複数の問題を参照していることを示すことができます。

@Issue("https://jira.org/issues/LO-531")
def 'single issue'() {

}

@Issue(["https://jira.org/issues/LO-531", "http://jira.org/issues/LO-123"])
def 'multiple issues'()

3.13. @Subject

そして最後に、 @Subject を使用して、どのクラスがテスト対象のクラスであるかを示すことができます。

@Subject
ItemService itemService // initialization here...

現在、これは情報提供のみを目的としています。

4. 拡張機能の構成

Spock構成ファイルでいくつかの拡張機能を構成できます。これには、各拡張機能の動作の説明が含まれます。

通常、Groovy 、たとえばSpockConfig.groovyと呼ばれるで構成ファイルを作成します。

もちろん、 Spockは構成ファイルを見つける必要があります。まず、 spock.configuration システムプロパティからカスタムの場所を読み取り、クラスパスでファイルを見つけようとします。 見つからない場合は、ファイルシステム内の場所に移動します。 それでも見つからない場合は、テスト実行クラスパスでSpockConfig.groovyを探します。

最終的に、SpockはSpockユーザーホームに移動します。これは、ホームディレクトリ内の単なるディレクトリ.spockです。 このディレクトリを変更するには、 spock.user.home というシステムプロパティを設定するか、環境変数SPOCK_USER_HOME。を使用します。

この例では、ファイル SpockConfig .groovy を作成し、それをクラスパス( src / test / resources / SpockConfig.Groovy )に配置します。

4.1. スタックトレースのフィルタリング

構成ファイルを使用することで、スタックトレースをフィルタリング(またはフィルタリングしない)できます。

runner {
    filterStackTrace false
}

デフォルト値はtrueです。

それがどのように機能し、練習するかを確認するために、 RuntimeException:をスローする簡単なテストを作成しましょう。

def 'stacktrace'() {
    expect:
    throw new RuntimeException("blabla")
}

filterStackTrace がfalseに設定されている場合、出力に次のように表示されます。

java.lang.RuntimeException: blabla

  at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
  at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
  at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
  at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
  at org.codehaus.groovy.reflection.CachedConstructor.invoke(CachedConstructor.java:83)
  at org.codehaus.groovy.runtime.callsite.ConstructorSite$ConstructorSiteNoUnwrapNoCoerce.callConstructor(ConstructorSite.java:105)
  at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCallConstructor(CallSiteArray.java:60)
  at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callConstructor(AbstractCallSite.java:235)
  at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callConstructor(AbstractCallSite.java:247)
  // 34 more lines in the stack trace...

このプロパティをtrue、に設定すると、次のようになります。

java.lang.RuntimeException: blabla

  at extensions.StackTraceTest.stacktrace(StackTraceTest.groovy:10)

覚えておいてくださいが、完全なスタックトレースを確認すると便利な場合があります。

4.2. Spock構成ファイルの条件付き機能

場合によっては、条件付きでスタックトレースをフィルタリングする必要があります。たとえば、継続的インテグレーションツールで完全なスタックトレースを表示する必要がありますが、これはローカルマシンでは必要ありません。

たとえば、環境変数に基づいて、単純な条件を追加できます。

if (System.getenv("FILTER_STACKTRACE") == null) {   
    filterStackTrace false
}

Spock構成ファイルはGroovyファイルであるため、Groovyコードのスニペットを含めることができます。

4.3. @IssueのプレフィックスとURL

以前、@Issueアノテーションについて説明しました。 構成ファイルを使用してこれを構成することもできます。 issueUrlPrefixを使用して共通のURL部分を定義します。 

他のプロパティは issueNamePrefix。 その後、すべて @問題値の前に issueNamePrefix 財産。

レポートに次の2つのプロパティを追加する必要があります。

report {
    issueNamePrefix 'Bug '
    issueUrlPrefix 'https://jira.org/issues/'
}

4.4. 実行順序の最適化

他の非常に便利なツールはoptimizeRunOrderです。 Spockは、失敗した仕様と、フィーチャメソッドの実行に必要な頻度と時間を記憶できます。

この知識に基づいて、 Spock は、最後の実行で失敗した機能を最初に実行します。 そもそも、失敗したスペックを次々と実行していきます。 さらに、最速のスペックが最初に実行されます。

この動作は、構成ファイルで有効になっている可能性があります。 オプティマイザーを有効にするには、optimizeRunOrderプロパティを使用します。

runner {
  optimizeRunOrder true
}

デフォルトでは、実行順序のオプティマイザは無効になっています。

4.5. 仕様の包含と除外

Spockは、特定の仕様を除外または含めることができます。 仕様クラスに適用されるクラス、スーパークラス、インターフェイス、またはアノテーションに頼ることができます。ライブラリは、機能レベルのアノテーションに基づいて、単一の機能を除外または含めることができます。

exclude プロパティを使用して、クラスTimeoutTestからテストスイートを簡単に除外できます。

import extensions.TimeoutTest

runner {
    exclude TimeoutTest
}

TimeoutTestとそのすべてのサブクラスが除外されます。TimeoutTest が仕様のクラスに適用された注釈である場合、この仕様は除外されます。

アノテーションと基本クラスを別々に指定できます。

import extensions.TimeoutTest
import spock.lang.Issue
    exclude {
        baseClass TimeoutTest
        annotation Issue
}

上記の例は、@ Issueアノテーションを持つテストクラスまたはメソッド、ならびにTimeoutTestまたはそのサブクラスのいずれかを除外します。

仕様を含めるには、includeプロパティを使用するだけです。 include のルールは、excludeと同じ方法で定義できます。

4.6. レポートの作成

テスト結果と既知のアノテーションに基づいて、Spockを使用してレポートを生成できます。さらに、このレポートには、 @ Title、@ See、@ Issue、@Narrativeなどが含まれます。値。

構成ファイルでレポートの生成を有効にできます。 デフォルトでは、レポートは生成されません。

私たちがしなければならないのは、いくつかのプロパティの値を渡すことだけです。

report {
    enabled true
    logFileDir '.'
    logFileName 'report.json'
    logFileSuffix new Date().format('yyyy-MM-dd')
}

上記のプロパティは次のとおりです。

  • 有効–レポートを生成するかどうか
  • logFileDir –レポートのディレクトリ
  • logFileName –レポートの名前
  • logFileSuffix –ダッシュで区切られた生成されたすべてのレポートベース名のサフィックス

enabledtrue、に設定する場合、logFileDirおよびlogFileNameプロパティを設定する必要があります。 logFileSuffixはオプションです。

これらすべてをシステムプロパティで設定することもできます: enabled spock.logFileDir、 spock.logFileName spock.logFileSuffix。

5. 結論

この記事では、最も一般的なSpock拡張機能について説明しました。

それらのほとんどが注釈に基づいていることを私たちは知っています。 さらに、 Spock 構成ファイルの作成方法と、使用可能な構成オプションについて学習しました。 つまり、新しく習得した知識は、効果的で読みやすいテストを書くのに非常に役立ちます。

すべての例の実装は、Githubプロジェクトにあります。