introduction to spock

90
Spock入門 G* ワークショップ Z 札幌 2014 2014/09/27

Upload: takahiro-sugiura

Post on 13-Dec-2014

789 views

Category:

Technology


1 download

DESCRIPTION

Spockの入門的な何か。

TRANSCRIPT

Page 1: Introduction to Spock

Spock入門G* ワークショップ Z 札幌 2014

2014/09/27

Page 2: Introduction to Spock

お前誰よ

• 名前:杉浦孝博 • twitter : @touchez_du_bois • 昼のお仕事: Grailsアプリ、C#アプリ、VB.NETアプリの保守 とあるサイトの運用、保守 時々開発

Page 3: Introduction to Spock

Today’s Goal. (本日の目標)

Page 4: Introduction to Spock

本日の目標• Spockってどんなもんか、「なんとなく」わかった気になる。 • Spockを使ってみたくなる。

Page 5: Introduction to Spock

Today’s Contents (本日の内容)

Page 6: Introduction to Spock

本日の内容• 入門編 • Spockとは • Spockの利点 • 構造 • Power Assert • データ駆動テスト • 相互作用中心のテスト

Page 7: Introduction to Spock

本日の内容• その他 • Spockの拡張機能 • Spockのモジュール • Spockと一緒に

• まとめ

Page 8: Introduction to Spock

Introduction (入門)

Page 9: Introduction to Spock

What’s Spock? (Spockとは)

Page 10: Introduction to Spock

Spockとは• Java, Groovy向けの、テスト・仕様フレームワーク。 • たいていのIDE、ビルドツール、CIサーバで使用可能。

Page 11: Introduction to Spock

Spockのバージョン• 安定バージョンは0.7。 • スナップショットで1.0がある。 • groovy 1.xと2.x用がある。

Page 12: Introduction to Spock

使い所• JavaやGroovyで作成したクラスの テストに。 • TDD(Test Driven Development)のフレームワークとして。 • BDD(Behavior Driven Development)のフレームワークとして。

Page 13: Introduction to Spock

Advantages of Spock (Spockの利点)

Page 14: Introduction to Spock

Structure (構造)

Page 15: Introduction to Spock

構造• クラス内の構造が決まっている。 • 役割ごとにメソッドが分かれている。 • メソッドの中も役割ごとに分かれている。 ⇒ 読みやすい構造、書きやすい構造。

Page 16: Introduction to Spock

Spockの構成要素• Specification • Fields • Fixture Methods • Feature Methods • Blocks • Helper Methods

Page 17: Introduction to Spock

Specification• 対象クラスのテスト内容・仕様を記述。 • spock.lang.Specificationクラスを継承。

import spock.lang.* !class TargetClassSpec extends Specification { // Fields // Fixture Methods // Feature Methods // Helper Methods }

Page 18: Introduction to Spock

Fields• テストで使用するオブジェクトを宣言。 • Featureメソッド実行ごとに初期化されるため、基本的に、インスタンス変数はFeatureメソッド間で共有できない。 • 共有したい場合、@Sharedを付ける。

@Shared def stack = new Stack()

Page 19: Introduction to Spock

Fixture Methods• テストの準備(setup)、後始末(cleanup)を行う。def setup() {} // 各Featureメソッドの実行前に実行 def cleanup() {} // 各Featureメソッドの実行後に実行 def setupSpec() {} // 最初のFeatureメソッドの実行前に実行 def cleanupSpec() {} // 最後のFeatureメソッドの実行後に実行

Page 20: Introduction to Spock

Feature Methods• テストの内容、仕様の内容を記述。 • 4つのフェーズからなり、それぞれBlockを記述。 • Setup(前処理、前提条件) • Stimulus(実行) • Response(検証) • Cleanup(後処理)

Page 21: Introduction to Spock

Block• フェーズに応じた、テスト、仕様のコードを記述。 • setup: / given: • when: • then: • expect: • cleanup: • and: • where:

Page 22: Introduction to Spock

BlockとPhaseBlock Phase

setup: / given: Setup

when: Stimulus

then: Response

expect: Stimulus + Response

cleanup: cleanup

where: -

and: -

Page 23: Introduction to Spock

when~thenの例def "push後のサイズは1"() { setup: def stack = new Stack<Integer>() when: stack.push(5) then: stack.size() == 1 }

Page 24: Introduction to Spock

expectの例

def "10と20で大きいのは20"() { expect: Math.max(10, 20) == 20 }

Page 25: Introduction to Spock

Helper Methods• Featureメソッドが大きくなった場合に、意味がある内容をメソッド化する。 • setupやcleanupも処理が大きくなった場合に、メソッド化する。

Page 26: Introduction to Spock

Advantages of Spock (Spockの利点)

Page 27: Introduction to Spock

Power Assert

(ぱわーあさーと)

Page 28: Introduction to Spock

Power Assert• テスト・仕様の内容を満たさない場合、各部分式の値を表示する。 • SpockからGroovy本体に取り込まれた。 ⇒ どこが違うか、どこから違うか、  ということが、比較的わかりやすい。

Page 29: Introduction to Spock

Power Assert def "maximum of two numbers"() { setup: def a = 2 def b = 1 def c = 1 expect: Math.max(a, b) == c }

Page 30: Introduction to Spock

Power Assert

Math.max(a, b) == c | | | | | 2 2 1 | 1 false

Page 31: Introduction to Spock

Power Assert @Unroll def "str1 == str2"() { setup: def str1 = "abcdefghijklmnopqrstuvwxyz" def str2 = "abcdefghijk1mnopqrstuvwxyz" expect: str1 == str2 }

Page 32: Introduction to Spock

Power Assert str1 == str2 | | | | | abcdefghijk1mnopqrstuvwxyz | false | 1 difference (96% similarity) | abcdefghijk(l)mnopqrstuvwxyz | abcdefghijk(1)mnopqrstuvwxyz abcdefghijklmnopqrstuvwxyz

Page 33: Introduction to Spock

Advantages of Spock (Spockの利点)

Page 34: Introduction to Spock

Data Driven Testing

(データ駆動テスト)

Page 35: Introduction to Spock

データ駆動テスト• 入力値と期待する結果の組み合わせを検証するために、同じテスト・仕様のコードを複数回実行したい場合に効果的。 • コードとデータを分離。 • コードを安易に複製しないで済む。 • データを自動生成したり、外部リソースから読み込んだりできる。

Page 36: Introduction to Spock

よくある例

def "maximum of two numbers"() { expect: Math.max(1, 3) == 3 Math.max(7, 4) == 4 Math.max(0, 0) == 0 }

• 入力値と期待値が異なるがコードは同じ。

Page 37: Introduction to Spock

データテーブル

def "maximum of two numbers"() { expect: Math.max(a, b) == c where: a | b | c // データ変数 1 | 3 | 3 // データ行 7 | 4 | 7 // 〃 0 | 0 | 0 // 〃 }

• コードとデータを分離。

// Math.max(1, 3) == 3 // Math.max(7, 4) == 7 // Math.max(0, 0) == 0 // の3イテレーション実施。

Page 38: Introduction to Spock

データテーブル

def "maximum of two numbers"() { expect: Math.max(a, b) == c where: a | b || c // データ変数 1 | 3 || 3 // データ行 7 | 4 || 7 // 〃 0 | 0 || 0 // 〃 }

• 入力値と期待値を(多少)わかりやすく。

Page 39: Introduction to Spock

どこが失敗?

def "maximum of two numbers"() { expect: Math.max(a, b) == c where: a | b || c 1 | 3 || 3 7 | 4 || 4 0 | 0 || 0 }

• 何回目のイテレーションで失敗したかわからない。

maximum of two numbers FAILED ! Condition not satisfied: ! Math.max(a, b) == c | | | | | 7 7 4 | 4 false

Page 40: Introduction to Spock

@Unroll

@Unroll def "maximum of two numbers"() { expect: Math.max(a, b) == c where: a | b || c 1 | 3 || 3 7 | 4 || 4 0 | 0 || 0 }

• @Unrollを付けたメソッドまたはクラスは、イテレーション毎にレポートされる。

maximum of two numbers[0] PASSED maximum of two numbers[1] FAILED ! Math.max(a, b) == c | | | | | 7 7 4 | 4 false ! maximum of two numbers[2] FAILED

Page 41: Introduction to Spock

もう少しわかりやすく

@Unroll def "maximum of #a and #b is #c"() { expect: Math.max(a, b) == c where: a | b || c 1 | 3 || 3 7 | 4 || 4 0 | 0 || 0 }

• メソッド名にプレースホルダを使うことで、レポート結果がわかりやすく。

maximum of 1 and 3 is 3 PASSED maximum of 7 and 4 is 4 FAILED ! Math.max(a, b) == c | | | | | 7 7 4 | 4 false ! maximum of 0 and 0 is 0 FAILED

Page 42: Introduction to Spock

データパイプ

def "maximum of two numbers"() { expect: Math.max(a, b) == c where: a << [ 1, 7, 0 ] b << [ 3, 4, 0 ] c << [ 3, 7, 0 ] }

• データ変数ごとにデータプロバイダを用意し、<<演算子で接続する。

// Collectionだけでなく、テキストファイルやデータベースなど、// 外部リソースから取得することも可能。

Page 43: Introduction to Spock

データパイプで複数の値

@Shared sql = Sql.newInstance("jdbc:h2:mem:", "org.h2.Driver") ! def "maximum of two numbers"() { expect: Math.max(a, b) == c where: [a, b, c] << sql.rows("select a, b, c from maxdata") }

• データプロバイダが複数の値を返す場合、複数のデータ変数に同時に接続可能。

Page 44: Introduction to Spock

データ変数へ代入

def "maximum of two numbers"() { expect: Math.max(a, b) == c where: a = 3 b = Math.random() * 100 c = a > b ? a : b }

• データ変数に直接値を代入することも可能。

Page 45: Introduction to Spock

Advantages of Spock (Spockの利点)

Page 46: Introduction to Spock

Interaction Based Testing (相互作用中心のテスト)

Page 47: Introduction to Spock

相互作用中心のテスト• Publish - Subscribeのように、オブジェクト同士の振る舞いをテスト・仕様化したい場合。 • 相互作用相手について、本物のオブジェクトを使うこともあるが、モックオブジェクトを使うこともある。

Page 48: Introduction to Spock

モックフレームワークの提供• 簡単に相互作用中心のテストを書けるように、Spock独自のモックフレームワークを提供。 • JMock, EasyMock, Mockitoといった既存のモックフレームワークと一緒に使うことも可能。

Page 49: Introduction to Spock

Spockでは• Spockでは、次の3つの方法を用意。 • モック(Mock) • スタブ(Stub) • スパイ(Spy)

Page 50: Introduction to Spock

プログラム例 class Publisher { List<Subscriber> subscribers = [] void send(String message) { subscribers.each { it.receive(message) } } void send(Event event) { subscribers.each { it.receive(event) } } } ! interface Subscriber { void receive(String message) void receive(Event event) }

Page 51: Introduction to Spock

モックオブジェクトの作成

// モックオブジェクトのインタフェース(クラス)を指定して作成 def subscriber = Mock(Subscriber) ! // 変数の型からモックオブジェクトのインタフェース(クラス)の // 型を推論 Subscriber subscriber = Mock()

• Mockメソッドで作成

Page 52: Introduction to Spock

モックオブジェクトの準備

class PublisherSpec extends Specification { Publisher publisher = new Publisher() Subscriber subscriber1 = Mock() Subscriber subscriber2 = Mock() ! def setup() { publisher.subscribers << subscriber1 publisher.subscribers << subscriber2 } }

• 例えば、こんな感じで。

Page 53: Introduction to Spock

モッキング

def "should send messages to all subscribers"() { when: publisher.send("hello") ! then: 1 * subscriber1.receive("hello") 1 * subscriber2.receive("hello") }

• テスト/仕様対象のオブジェクトと、相互作用するオブジェクトの間の、インタラクション(相互作用)を宣言すること。

Page 54: Introduction to Spock

インタラクション

1 * subscriber.receive("hello") | | | | | | | argument constraint | | method constraint | target constraint cardinality

• 多重度(cardinality)、対象制約(target constraint)、メソッド制約(method constraint)、引数制約(argument constraint)からなる。

Page 55: Introduction to Spock

多重度

1 * subscriber.receive("hello") // 1回 0 * subscriber.receive("hello") // 0回 (1..3) * subscriber.receive("hello") // 1〜~3回 (1.._) * subscriber.receive("hello") // 1回以上 (_..3) * subscriber.receive("hello") // 3回以下 _ * subscriber.receive("hello") // 0回以上

• 何回メソッド呼び出しが行われるかを指定する。 • 固定の数値や範囲を指定可能。

Page 56: Introduction to Spock

対象制約

1 * subscriber1.receive("hello") // subscriber1に対して 1 * _.receive("hello") // 任意のモックオブジェクト

• どのオブジェクト(モックオブジェクト)を対象にするかを指定する。

Page 57: Introduction to Spock

メソッド制約

1 * subscriber.receive("hello") // receiveメソッド 1 * subscriber._("hello") // 任意のメソッド 1 * subscriber./r.*e/("hello") // rで始まりeで終わるメソッド

• どのメソッド呼び出しを対象にするかを指定する。

Page 58: Introduction to Spock

引数制約// 引数が"hello"という文字列 1 * subscriber.receive("hello") // 引数が"hello"という文字列ではない 1 * subscriber.receive(!"hello") // 引数なし 1 * subscriber.receive() // 1引数(nullも含む) 1 * subscriber.receive(_) // 任意の引数 1 * subscriber.receive(*_) // nullではない 1 * subscriber.receive(!null) // String型の引数(nullは含まれない) 1 * subscriber.receive(_ as String) // 指定された条件を満たす引数 1 * subscriber.receive({ it.size() > 3 })

• どんな引数を期待するかを指定する。

Page 59: Introduction to Spock

スタビング• 特定のメソッド呼び出しに対する応答を宣言すること。 • 何回呼ばれても、特定の値を返したり、何らかの副作用が働くようにする。

Page 60: Introduction to Spock

スタビング• モッキングに比べ、多重度の指定がない代わりに、レスポンスジェネレータ(Response Generator)を指定する。

subscriber1.receive(_) >> "ok" | | | | | | | response generator | | argument constraint | method constraint target constraint

Page 61: Introduction to Spock

スタビングの例

def setup() { publisher.subscribers << subscriber1 publisher.subscribers << subscriber2 ! // receiveメソッドの戻り値は常に"ok"とする subscriber1.receive(_) >> "ok" subscriber2.receive(_) >> "ok" }

interface Subscriber { // String型の値を返すように変更 String receive(String message) String receive(Event message) }

Page 62: Introduction to Spock

一連の値を返す• >>>演算子の後に、リストなどイテレーティブな値を指定する。

// 1回目: "ok" // 2回目: "error" // 3回目: "error" // 4回目以降: "ok" subscriber1.receive(_) >>> ["ok", "error", "error", "ok"]

Page 63: Introduction to Spock

動的に値を返す• >>演算子の後に、動的な値を返すクロージャを指定する。

subscriber1.receive(_) >> { String message -> message.size() > 3 ? "ok" : "fail" }

Page 64: Introduction to Spock

副作用の実行• >>演算子の後のクロージャ中に、副作用としての処理を記述する。

subscriber1.receive(_) >> { String message -> println message throw new InvalidArgumentException() }

Page 65: Introduction to Spock

組み合わせ• モッキングとスタビングを組み合わせて使うことも可能。

1 * subscriber1.receive("message1") >> "ok" 1 * subscriber2.receive("message2") >> "fail"

Page 66: Introduction to Spock

スタブオブジェクトの作成

// スタブオブジェクトのインタフェース(クラス)を指定して作成 def subscriber = Stub(Subscriber) ! // 変数の型からスタブオブジェクトのインタフェース(クラス)の // 型を推論 Subscriber subscriber = Stub()

• Stubメソッドで作成する。 • Mockメソッドで作成した場合、モッキングもスタビングもできるが、Stubメソッドで作成した場合はスタビングのみ。

Page 67: Introduction to Spock

スパイ• 本物のオブジェクトを監視して、モッキングやスタビングを行う。 • メソッド呼び出しは、本物のメソッド呼び出しに移譲され、その際の戻り値がスパイを経由して返される。

Page 68: Introduction to Spock

スパイオブジェクトの作成

// スパイ対象のクラスを指定して作成 def subscriber = Spy(SubscriberImpl, constructorArgs: ["Fred"])

• Spyメソッドで作成

Page 69: Introduction to Spock

スパイでのスタビング

// スパイ対象のクラスを指定して作成 def subscriber = Spy(SubscriberImpl, constructorArgs: ["Fred"]) ! // スタビング。本物のreceiveメソッドは呼ばれない。 subscriber.receive(_) >> "ok"

• スパイで普通にスタビングを行うと、本物のメソッドが呼ばれなくなる。

Page 70: Introduction to Spock

スタビング+本物のメソッド

// 本物のreceiveメソッドが呼ばれた後、 // 戻り値を動的に変更 subscriber.receive(_) >> { String message -> callRealMethod() message.size() > 3 ? "ok" : "fail" }

• callRealMethodメソッド、あるいはcallRealMethodWithArgsメソッドを使うことで、任意のコードの実行と、本物のメソッドへの委譲が行われる。

Page 71: Introduction to Spock

スタビング+本物のメソッド

// 本物のreceiveメソッドを、違う引数で実行し、 // 戻り値を動的に変更 subscriber.receive(_) >> { String message -> callRealMethodWithArgs("Changed message") message.size() > 3 ? "ok" : "fail" }

• callRealMethodメソッドとcallRealMethodWithArgsメソッドの違いは、後者は呼び出し時の引数を差し替えたい場合に使用する。

Page 72: Introduction to Spock

et cetera (その他)

Page 73: Introduction to Spock

Spock Extensions (Spock拡張機能)

Page 74: Introduction to Spock

Spockの拡張機能• Spockの動作を拡張したり変更したりすることができる。 • 仕組みとして、ビルトイン機能拡張と、カスタム機能拡張がある。

Page 75: Introduction to Spock

ビルトイン拡張機能• ビルトイン拡張機能のほとんどは、アノテーションを通して使用する。 • @Unrollもその一例。 • 他にも、@Ignor, @IgnorRest, @IgnorIf, @Requiresなどがある。 • 自作も可能。

Page 76: Introduction to Spock

カスタム拡張機能※詳細不明のため省略

Page 77: Introduction to Spock

Spock Modules (Spockモジュール)

Page 78: Introduction to Spock

Spockのモジュール• Spockの動作を拡張したり変更したりすることができる。 • githubで、SpockのCore以外にもいくつかモジュールを提供している。

Page 79: Introduction to Spock

純正なもの• spock-unitils • Unitilsというテスト用ユーティリティをSpockと統合したもの。

• spock-spring, spock-boot • それぞれ、Spring TestContextやSpring Bootと統合したもの。

Page 80: Introduction to Spock

純正でないもの• Spock Report Extension • 実行結果のレポートを作成するためのグローバル拡張機能。 • https://github.com/renatoathaydes/spock-reports

Page 81: Introduction to Spock

With Spock (Spockと一緒に)

Page 82: Introduction to Spock

Spockと組み合わせる• テスティングフレームワークの中には、Spockをサポートしていたり、Spockと組み合わせて使ったりするものがある。

Page 83: Introduction to Spock

Geb + Spock• Gebとは、Webブラウザを使うWebアプリケーションの操作を自動化・自動実行するためのソフトウェア。 • Geb用のSpecificationクラスを提供。

Page 84: Introduction to Spock

Arquillian + Spock• Arquillianとは、自動テストでJavaEEコンテナを利用可能にするためのソフトウェア。 • ArquillianをSpockと組み合わせて使えるように、アノテーションなど機能拡張を提供。

Page 85: Introduction to Spock

Robospock• Robospockとは、Spockを使ってAndroidアプリケーションをテストするためのソフトウェア。 • Android用のユニットテストフレームワークであるRobolectricとSpockを組み合わせ、Specificationクラスを提供。

Page 86: Introduction to Spock

To wrap up (まとめ)

Page 87: Introduction to Spock

まとめ• Spockは、テスト・仕様が書きやすく読みやすいフレームワークです。 • JUnitの代わりにユニットテストで使う等、導入しやすい所から初めてみるのはいかがでしょうか。 !

Page 88: Introduction to Spock

参考URL• 本家 • https://code.google.com/p/spock/

• ソースコード • https://github.com/spockframework/spock

• リファレンスドキュメント(英語版) • http://spock-framework.readthedocs.org/en/latest/

• リファレンスドキュメント(日本語版) • http://spock-framework-reference-documentation-ja.readthedocs.org/ja/latest/

• G*ワークショップZ May 2013 - Spockハンズオンの資料 • https://github.com/spockframework/spock

Page 89: Introduction to Spock

参考URL• Geb • http://www.gebish.org/

• Arquillian • http://arquillian.org/

• Arquillian TestRunner Spock • http://arquillian.org/modules/spock-test-runner/

• RoboSpock • http://robospock.org/

Page 90: Introduction to Spock

ご清聴ありがとうございました