Introduction to Spock

5,467 views

Published on

Spockの入門的な何か。

Published in: Technology
0 Comments
7 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
5,467
On SlideShare
0
From Embeds
0
Number of Embeds
3,223
Actions
Shares
0
Downloads
11
Comments
0
Likes
7
Embeds 0
No embeds

No notes for slide

Introduction to Spock

  1. 1. Spock入門 G* ワークショップ Z 札幌 2014 2014/09/27
  2. 2. お前誰よ • 名前:杉浦孝博 • twitter : @touchez_du_bois • 昼のお仕事: Grailsアプリ、C#アプリ、VB.NETアプリの保守 とあるサイトの運用、保守 時々開発
  3. 3. Today’s Goal. (本日の目標)
  4. 4. 本日の目標 • Spockってどんなもんか、 「なんとなく」わかった気になる。 • Spockを使ってみたくなる。
  5. 5. Today’s Contents (本日の内容)
  6. 6. 本日の内容 • 入門編 • Spockとは • Spockの利点 • 構造 • Power Assert • データ駆動テスト • 相互作用中心のテスト
  7. 7. 本日の内容 • その他 • Spockの拡張機能 • Spockのモジュール • Spockと一緒に • まとめ
  8. 8. Introduction (入門)
  9. 9. What’s Spock? (Spockとは)
  10. 10. Spockとは • Java, Groovy向けの、テスト・仕様 フレームワーク。 • たいていのIDE、ビルドツール、CI サーバで使用可能。
  11. 11. Spockのバージョン • 安定バージョンは0.7。 • スナップショットで1.0がある。 • groovy 1.xと2.x用がある。
  12. 12. 使い所 • JavaやGroovyで作成したクラスの テストに。 • TDD(Test Driven Development)のフ レームワークとして。 • BDD(Behavior Driven Development) のフレームワークとして。
  13. 13. Advantages of Spock (Spockの利点)
  14. 14. Structure (構造)
  15. 15. 構造 • クラス内の構造が決まっている。 • 役割ごとにメソッドが分かれている。 • メソッドの中も役割ごとに分かれてい る。 ⇒ 読みやすい構造、書きやすい構造。
  16. 16. Spockの構成要素 • Specification • Fields • Fixture Methods • Feature Methods • Blocks • Helper Methods
  17. 17. Specification • 対象クラスのテスト内容・仕様を記述。 • spock.lang.Specificationクラスを継 承。 import spock.lang.* ! class TargetClassSpec extends Specification { // Fields // Fixture Methods // Feature Methods // Helper Methods }
  18. 18. Fields • テストで使用するオブジェクトを宣言。 • Featureメソッド実行ごとに初期化され るため、基本的に、インスタンス変数は Featureメソッド間で共有できない。 • 共有したい場合、@Sharedを付ける。 @Shared def stack = new Stack()
  19. 19. Fixture Methods • テストの準備(setup)、後始末(cleanup) を行う。 def setup() {} // 各Featureメソッドの実行前に実行 def cleanup() {} // 各Featureメソッドの実行後に実行 def setupSpec() {} // 最初のFeatureメソッドの実行前に実行 def cleanupSpec() {} // 最後のFeatureメソッドの実行後に実行
  20. 20. Feature Methods • テストの内容、仕様の内容を記述。 • 4つのフェーズからなり、それぞれBlock を記述。 • Setup(前処理、前提条件) • Stimulus(実行) • Response(検証) • Cleanup(後処理)
  21. 21. Block • フェーズに応じた、テスト、仕様のコー ドを記述。 • setup: / given: • when: • then: • expect: • cleanup: • and: • where:
  22. 22. BlockとPhase Block Phase setup: / given: Setup when: Stimulus then: Response expect: Stimulus + Response cleanup: cleanup where: - and: -
  23. 23. when~thenの例 def "push後のサイズは1"() { setup: def stack = new Stack<Integer>() when: stack.push(5) then: stack.size() == 1 }
  24. 24. expectの例 def "10と20で大きいのは20"() { expect: Math.max(10, 20) == 20 }
  25. 25. Helper Methods • Featureメソッドが大きくなった場合 に、意味がある内容をメソッド化する。 • setupやcleanupも処理が大きくなった 場合に、メソッド化する。
  26. 26. Advantages of Spock (Spockの利点)
  27. 27. Power Assert (ぱわーあさーと)
  28. 28. Power Assert • テスト・仕様の内容を満たさない場合、 各部分式の値を表示する。 • SpockからGroovy本体に取り込まれ た。 ⇒ どこが違うか、どこから違うか、   ということが、比較的わかりやすい。
  29. 29. Power Assert def "maximum of two numbers"() { setup: def a = 2 def b = 1 def c = 1 expect: Math.max(a, b) == c }
  30. 30. Power Assert Math.max(a, b) == c | | | | | 2 2 1 | 1 false
  31. 31. Power Assert @Unroll def "str1 == str2"() { setup: def str1 = "abcdefghijklmnopqrstuvwxyz" def str2 = "abcdefghijk1mnopqrstuvwxyz" expect: str1 == str2 }
  32. 32. Power Assert str1 == str2 | | | | | abcdefghijk1mnopqrstuvwxyz | false | 1 difference (96% similarity) | abcdefghijk(l)mnopqrstuvwxyz | abcdefghijk(1)mnopqrstuvwxyz abcdefghijklmnopqrstuvwxyz
  33. 33. Advantages of Spock (Spockの利点)
  34. 34. Data Driven Testing (データ駆動テスト)
  35. 35. データ駆動テスト • 入力値と期待する結果の組み合わせを検 証するために、同じテスト・仕様のコー ドを複数回実行したい場合に効果的。 • コードとデータを分離。 • コードを安易に複製しないで済む。 • データを自動生成したり、外部リソース から読み込んだりできる。
  36. 36. よくある例 • 入力値と期待値が異なるがコードは同じ。 def "maximum of two numbers"() { expect: Math.max(1, 3) == 3 Math.max(7, 4) == 4 Math.max(0, 0) == 0 }
  37. 37. データテーブル • コードとデータを分離。 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イテレーション実施。
  38. 38. データテーブル • 入力値と期待値を(多少)わかりやすく。 def "maximum of two numbers"() { expect: Math.max(a, b) == c where: a | b || c // データ変数 1 | 3 || 3 // データ行 7 | 4 || 7 // 〃 0 | 0 || 0 // 〃 }
  39. 39. どこが失敗? • 何回目のイテレーションで失敗したか わからない。 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
  40. 40. @Unroll • @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 } 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
  41. 41. もう少しわかりやすく • メソッド名にプレースホルダを使うこと で、レポート結果がわかりやすく。 @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
  42. 42. データパイプ • データ変数ごとにデータプロバイダを用 意し、<<演算子で接続する。 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だけでなく、テキストファイルやデータベースなど、 // 外部リソースから取得することも可能。
  43. 43. データパイプで複数の値 • データプロバイダが複数の値を返す場合、 複数のデータ変数に同時に接続可能。 @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") }
  44. 44. データ変数へ代入 • データ変数に直接値を代入することも可 能。 def "maximum of two numbers"() { expect: Math.max(a, b) == c where: a = 3 b = Math.random() * 100 c = a > b ? a : b }
  45. 45. Advantages of Spock (Spockの利点)
  46. 46. Interaction Based Testing (相互作用中心のテスト)
  47. 47. 相互作用中心のテスト • Publish - Subscribeのように、 オブジェクト同士の振る舞いをテスト・ 仕様化したい場合。 • 相互作用相手について、 本物のオブジェクトを使うこともあるが、 モックオブジェクトを使うこともある。
  48. 48. モックフレームワークの提供 • 簡単に相互作用中心のテストを書けるよ うに、Spock独自のモックフレームワー クを提供。 • JMock, EasyMock, Mockitoといった既 存のモックフレームワークと一緒に使う ことも可能。
  49. 49. Spockでは • Spockでは、次の3つの方法を用意。 • モック(Mock) • スタブ(Stub) • スパイ(Spy)
  50. 50. プログラム例 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) }
  51. 51. モックオブジェクトの作成 • Mockメソッドで作成 // モックオブジェクトのインタフェース(クラス)を指定して作成 def subscriber = Mock(Subscriber) ! // 変数の型からモックオブジェクトのインタフェース(クラス)の // 型を推論 Subscriber subscriber = Mock()
  52. 52. モックオブジェクトの準備 • 例えば、こんな感じで。 class PublisherSpec extends Specification { Publisher publisher = new Publisher() Subscriber subscriber1 = Mock() Subscriber subscriber2 = Mock() ! def setup() { publisher.subscribers << subscriber1 publisher.subscribers << subscriber2 } }
  53. 53. モッキング • テスト/仕様対象のオブジェクトと、相互 作用するオブジェクトの間の、インタラ クション(相互作用)を宣言すること。 def "should send messages to all subscribers"() { when: publisher.send("hello") ! then: 1 * subscriber1.receive("hello") 1 * subscriber2.receive("hello") }
  54. 54. インタラクション • 多重度(cardinality)、対象制約(target constraint)、メソッド制約(method constraint)、引数制約(argument constraint)からなる。 1 * subscriber.receive("hello") | | | | | | | argument constraint | | method constraint | target constraint cardinality
  55. 55. 多重度 • 何回メソッド呼び出しが行われるかを指 定する。 • 固定の数値や範囲を指定可能。 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回以上
  56. 56. 対象制約 • どのオブジェクト(モックオブジェクト) を対象にするかを指定する。 1 * subscriber1.receive("hello") // subscriber1に対して 1 * _.receive("hello") // 任意のモックオブジェクト
  57. 57. メソッド制約 • どのメソッド呼び出しを対象にするかを 指定する。 1 * subscriber.receive("hello") // receiveメソッド 1 * subscriber._("hello") // 任意のメソッド 1 * subscriber./r.*e/("hello") // rで始まりeで終わるメソッド
  58. 58. 引数制約 • どんな引数を期待するかを指定する。 // 引数が"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 })
  59. 59. スタビング • 特定のメソッド呼び出しに対する応答を 宣言すること。 • 何回呼ばれても、特定の値を返したり、 何らかの副作用が働くようにする。
  60. 60. スタビング • モッキングに比べ、多重度の指定がない 代わりに、レスポンスジェネレータ (Response Generator)を指定する。 subscriber1.receive(_) >> "ok" | | | | | | | response generator | | argument constraint | method constraint target constraint
  61. 61. スタビングの例 interface Subscriber { def setup() { publisher.subscribers << subscriber1 publisher.subscribers << subscriber2 ! // receiveメソッドの戻り値は常に"ok"とする subscriber1.receive(_) >> "ok" subscriber2.receive(_) >> "ok" } // String型の値を返すように変更 String receive(String message) String receive(Event message) }
  62. 62. 一連の値を返す • >>>演算子の後に、リストなどイテレー ティブな値を指定する。 // 1回目: "ok" // 2回目: "error" // 3回目: "error" // 4回目以降: "ok" subscriber1.receive(_) >>> ["ok", "error", "error", "ok"]
  63. 63. 動的に値を返す • >>演算子の後に、動的な値を返すクロー ジャを指定する。 subscriber1.receive(_) >> { String message -> message.size() > 3 ? "ok" : "fail" }
  64. 64. 副作用の実行 • >>演算子の後のクロージャ中に、副作用 としての処理を記述する。 subscriber1.receive(_) >> { String message -> println message throw new InvalidArgumentException() }
  65. 65. 組み合わせ • モッキングとスタビングを組み合わせて 使うことも可能。 1 * subscriber1.receive("message1") >> "ok" 1 * subscriber2.receive("message2") >> "fail"
  66. 66. スタブオブジェクトの作成 • Stubメソッドで作成する。 • Mockメソッドで作成した場合、モッキ ングもスタビングもできるが、Stubメ ソッドで作成した場合はスタビングのみ。 // スタブオブジェクトのインタフェース(クラス)を指定して作成 def subscriber = Stub(Subscriber) ! // 変数の型からスタブオブジェクトのインタフェース(クラス)の // 型を推論 Subscriber subscriber = Stub()
  67. 67. スパイ • 本物のオブジェクトを監視して、モッキ ングやスタビングを行う。 • メソッド呼び出しは、本物のメソッド呼 び出しに移譲され、その際の戻り値がス パイを経由して返される。
  68. 68. スパイオブジェクトの作成 • Spyメソッドで作成 // スパイ対象のクラスを指定して作成 def subscriber = Spy(SubscriberImpl, constructorArgs: ["Fred"])
  69. 69. スパイでのスタビング • スパイで普通にスタビングを行うと、本 物のメソッドが呼ばれなくなる。 // スパイ対象のクラスを指定して作成 def subscriber = Spy(SubscriberImpl, constructorArgs: ["Fred"]) ! // スタビング。本物のreceiveメソッドは呼ばれない。 subscriber.receive(_) >> "ok"
  70. 70. スタビング+本物のメソッド • callRealMethodメソッド、あるいは callRealMethodWithArgsメソッドを使 うことで、任意のコードの実行と、本物 のメソッドへの委譲が行われる。 // 本物のreceiveメソッドが呼ばれた後、 // 戻り値を動的に変更 subscriber.receive(_) >> { String message -> callRealMethod() message.size() > 3 ? "ok" : "fail" }
  71. 71. スタビング+本物のメソッド • callRealMethodメソッドと callRealMethodWithArgsメソッドの違 いは、後者は呼び出し時の引数を差し替 えたい場合に使用する。 // 本物のreceiveメソッドを、違う引数で実行し、 // 戻り値を動的に変更 subscriber.receive(_) >> { String message -> callRealMethodWithArgs("Changed message") message.size() > 3 ? "ok" : "fail" }
  72. 72. et cetera (その他)
  73. 73. Spock Extensions (Spock拡張機能)
  74. 74. Spockの拡張機能 • Spockの動作を拡張したり変更した りすることができる。 • 仕組みとして、ビルトイン機能拡張 と、カスタム機能拡張がある。
  75. 75. ビルトイン拡張機能 • ビルトイン拡張機能のほとんどは、 アノテーションを通して使用する。 • @Unrollもその一例。 • 他にも、@Ignor, @IgnorRest, @IgnorIf, @Requiresなどがある。 • 自作も可能。
  76. 76. カスタム拡張機能 ※詳細不明のため省略
  77. 77. Spock Modules (Spockモジュール)
  78. 78. Spockのモジュール • Spockの動作を拡張したり変更した りすることができる。 • githubで、SpockのCore以外にもい くつかモジュールを提供している。
  79. 79. 純正なもの • spock-unitils • Unitilsというテスト用ユーティリ ティをSpockと統合したもの。 • spock-spring, spock-boot • それぞれ、Spring TestContext やSpring Bootと統合したもの。
  80. 80. 純正でないもの • Spock Report Extension • 実行結果のレポートを作成するた めのグローバル拡張機能。 • https://github.com/ renatoathaydes/spock-reports
  81. 81. With Spock (Spockと一緒に)
  82. 82. Spockと組み合わせる • テスティングフレームワークの中に は、Spockをサポートしていたり、 Spockと組み合わせて使ったりする ものがある。
  83. 83. Geb + Spock • Gebとは、Webブラウザを使うWeb アプリケーションの操作を自動化・ 自動実行するためのソフトウェア。 • Geb用のSpecificationクラスを提供。
  84. 84. Arquillian + Spock • Arquillianとは、自動テストでJavaEE コンテナを利用可能にするためのソ フトウェア。 • ArquillianをSpockと組み合わせて使 えるように、アノテーションなど機 能拡張を提供。
  85. 85. Robospock • Robospockとは、Spockを使って Androidアプリケーションをテストする ためのソフトウェア。 • Android用のユニットテストフレームワー クであるRobolectricとSpockを組み合 わせ、Specificationクラスを提供。
  86. 86. To wrap up (まとめ)
  87. 87. まとめ • Spockは、テスト・仕様が書きやす く読みやすいフレームワークです。 • JUnitの代わりにユニットテストで使 う等、導入しやすい所から初めてみ るのはいかがでしょうか。 !
  88. 88. 参考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
  89. 89. 参考URL • Geb • http://www.gebish.org/ • Arquillian • http://arquillian.org/ • Arquillian TestRunner Spock • http://arquillian.org/modules/spock-test-runner/ • RoboSpock • http://robospock.org/
  90. 90. ご清聴 ありがとう ございました

×