[from now] 2013/05/07 18:35:04

diff --git a/src/main/groovy/bank/Account.groovy b/src/main/groovy/bank/Account.groovy
new file mode 100644
index 0000000..715a130
--- /dev/null
+++ b/src/main/groovy/bank/Account.groovy
@@ -0,0 +1,44 @@
+package bank
+
+/**
+ * 口座を表すクラスです。
+ */
+class Account {
+
+    /** 残高 */
+    BigDecimal balance
+
+    Account() {
+        this(0)
+    }
+
+    Account(BigDecimal balance) {
+        this.balance = balance
+    }
+
+    /**
+     * 引き出します。
+     *
+     * @param amount 金額
+     * @throws InsufficientFundsException 残高不足の場合
+     */
+    def debit(BigDecimal amount) {
+        if (amount > balance) {
+            throw new InsufficientFundsException("残金不足です")
+        }
+        balance -= amount
+    }
+
+    /**
+     * 預け入れます。
+     *
+     * @param amount 金額
+     */
+    def credit(BigDecimal amount) {
+        balance += amount
+    }
+
+    Boolean isEmpty() {
+        !balance
+    }
+}
diff --git a/src/main/groovy/bank/InsufficientFundsException.groovy b/src/main/groovy/bank/InsufficientFundsException.groovy
new file mode 100644
index 0000000..6ba1026
--- /dev/null
+++ b/src/main/groovy/bank/InsufficientFundsException.groovy
@@ -0,0 +1,7 @@
+package bank
+
+import groovy.transform.InheritConstructors
+
+@InheritConstructors
+class InsufficientFundsException extends RuntimeException{
+}
diff --git a/src/main/groovy/mock/Publisher.groovy b/src/main/groovy/mock/Publisher.groovy
new file mode 100644
index 0000000..552866c
--- /dev/null
+++ b/src/main/groovy/mock/Publisher.groovy
@@ -0,0 +1,8 @@
+package mock
+
+class Publisher {
+    List<Subscriber> subscribers = []
+    void send(String message) {
+        subscribers.each { it.receive(message) }
+    }
+}
diff --git a/src/main/groovy/mock/Subscriber.groovy b/src/main/groovy/mock/Subscriber.groovy
new file mode 100644
index 0000000..3ffa766
--- /dev/null
+++ b/src/main/groovy/mock/Subscriber.groovy
@@ -0,0 +1,7 @@
+package mock
+
+class Subscriber {
+    String receive(String message) {
+        "received"
+    }
+}
diff --git a/src/test/groovy/Hoge.groovy b/src/test/groovy/Hoge.groovy
new file mode 100644
index 0000000..b030938
--- /dev/null
+++ b/src/test/groovy/Hoge.groovy
@@ -0,0 +1,8 @@
+import spock.lang.Specification
+
+class Hoge extends Specification {
+
+    def "xxx"() {
+        expect: true
+    }
+}
diff --git a/src/test/groovy/basic/Basic01_BlockSpec.groovy b/src/test/groovy/basic/Basic01_BlockSpec.groovy
new file mode 100644
index 0000000..5dd35ea
--- /dev/null
+++ b/src/test/groovy/basic/Basic01_BlockSpec.groovy
@@ -0,0 +1,98 @@
+package basic
+
+import org.spockframework.compiler.InvalidSpecCompileException
+import util.EmbeddedSpec
+
+/**
+ * ブロックの使い方
+ */
+class Basic01_BlockSpec extends EmbeddedSpec {
+
+    def "when-thenを使用した場合"() {
+        setup: true
+        when: true
+        then: true
+        cleanup: true
+    }
+
+    def "expectを使用した場合"() {
+        setup: true
+        expect: true
+        cleanup: true
+    }
+
+    def "setup/cleanupは省略できる"() {
+        when: true
+        then: true
+    }
+
+    def "stimulusとresponseのセットは繰り返すことができる"() {
+        when: true
+        then: true
+        expect: true
+        when: true
+        then: true
+        when: true
+        then: true
+        expect: true
+    }
+
+    def "ただしexpectブロックは連続して使用できない"() {
+        when:
+        compiler.compileFeatureBody """
+            expect: true
+            expect: true
+        """
+
+        then:
+        InvalidSpecCompileException e = thrown()
+        e.message == "'expect' is not allowed here; instead, use one of: [and, when, cleanup, where, end-of-method] @ line 2, column 21."
+    }
+
+    def "and:を使用して前のブロックを継続できる"() {
+        setup: true
+        and: true
+        and: true
+
+        when: true
+        and: true
+        and: true
+
+        then: true
+        and: true
+        and: true
+
+        expect: true
+        and: true
+        and: true
+
+        cleanup: true
+        and: true
+        and: true
+    }
+
+    def "ブロックにはドキュメントが設定できる"() {
+        setup: "スタックが空"
+        def stack = new Stack()
+
+        when: "要素を一つ追加"
+        stack.push("spock")
+
+        then: "スタックが空でない"
+        !stack.empty()
+
+        and: "スタックのサイズが1"
+        stack.size() == 1
+
+        and: "先頭要素はspock"
+        stack.peek() == "spock"
+    }
+
+    def "xx"() {
+        setup:
+        def x = "hello"
+        def y = "hallo"
+        expect:
+        x == y
+    }
+}
diff --git a/src/test/groovy/basic/Basic02_SetupSpec.groovy b/src/test/groovy/basic/Basic02_SetupSpec.groovy
new file mode 100644
index 0000000..23e831a
--- /dev/null
+++ b/src/test/groovy/basic/Basic02_SetupSpec.groovy
@@ -0,0 +1,47 @@
+package basic
+
+import org.spockframework.compiler.InvalidSpecCompileException
+import util.EmbeddedSpec
+
+/**
+ * セットアップブロック
+ */
+class Basic02_SetupSpec extends EmbeddedSpec {
+
+    def "基本的な使い方"() {
+        setup:
+        true
+    }
+
+
+    def "givenを使うこともできる"() {
+        given:
+        true
+    }
+
+    def "stimulusより前のコードは暗黙的にsetupブロックとなる"() {
+        // ここはsetupブロック
+        true
+
+        expect:
+        true
+    }
+
+    def "暗黙的なsetupブロックと明示的なsetupブロックを組み合わせて使用出来る"() {
+        true
+
+        setup:
+        true
+    }
+
+    def "複数のsetupブロックを宣言することはできない"() {
+        when:
+        compiler.compileFeatureBody """
+            setup: true
+            setup: true
+        """
+
+        then:
+        thrown(InvalidSpecCompileException)
+    }
+}
diff --git a/src/test/groovy/basic/Basic03_WhenThenSpec.groovy b/src/test/groovy/basic/Basic03_WhenThenSpec.groovy
new file mode 100644
index 0000000..4807643
--- /dev/null
+++ b/src/test/groovy/basic/Basic03_WhenThenSpec.groovy
@@ -0,0 +1,103 @@
+package basic
+
+import org.spockframework.runtime.SpockComparisonFailure
+import util.EmbeddedSpec
+
+/**
+ * When-Thenブロック
+ */
+class Basic03_WhenThenSpec extends EmbeddedSpec {
+
+    Stack stack = new Stack()
+
+    def "When-Thenの使い方"() {
+        when: "whenブロックは「もし...」を表す任意の実行コードを記述する"
+        stack.push("spock")
+
+        then: "thenブロックにはコンディションを記述する"
+        !stack.empty
+        stack.size() == 1
+        stack.peek() == "spock"
+    }
+
+    def "コンディションは単なるBoolean式で記述する"() {
+        when:
+        true
+
+        then: "Boolean式はGroovy流に評価される"
+        !false
+        true
+        !0
+        1
+        ![:]
+        ['one': 1]
+        ![]
+        [1]
+        !""
+        "this is true"
+        new Object()
+        !null
+    }
+
+    def "コンディションが失敗した場合はパワーアサートの出力"() {
+        when:
+        runner.runFeatureBody """
+            when:
+            def x = 1
+            def y = 2
+            then:
+            x == y
+        """
+
+        then:
+        SpockComparisonFailure e = thrown()
+        e.condition.rendering.trim() == """
+                                        |x == y
+                                        || |  |
+                                        |1 |  2
+                                        |  false
+                                        """.stripMargin().trim()
+    }
+
+    def "トップレベルの式のみがコンディションとして認識される"() {
+        when:
+        def list = [1, 2, 3]
+
+        then: "トップレベル以外の式はコンディションとして認識されない"
+        list.each {
+            it > 0 // これはコンティションとして認識されない
+        }
+
+        and: "回避方法1: 明示的なアサート"
+        list.each {
+            assert it > 0
+        }
+
+        and: "回避方法2: トップレベルの式で評価行えるようにする"
+        list.every {
+            it > 0
+        }
+    }
+
+    def "コンディションのグループ化"() {
+        setup:
+        def stack = new Stack()
+
+        when:
+        stack.push("spock")
+
+        then: "withブロックを使ってグループ化"
+        with(stack) {
+            !empty()
+            size() == 1
+            peek() == "spock"
+        }
+    }
+
+    def "実はthenブロックを続けることができるが詳しくはモックの中で"() {
+        when: true
+        then: true
+        then: true
+        then: true
+    }
+}
\ No newline at end of file
diff --git a/src/test/groovy/basic/Basic04_ExceptionSpec.groovy b/src/test/groovy/basic/Basic04_ExceptionSpec.groovy
new file mode 100644
index 0000000..b0dde86
--- /dev/null
+++ b/src/test/groovy/basic/Basic04_ExceptionSpec.groovy
@@ -0,0 +1,51 @@
+package basic
+
+import util.EmbeddedSpec
+
+/**
+ * 例外のコンディション
+ */
+class Basic04_ExceptionSpec extends EmbeddedSpec {
+
+    def "例外コンディションの基本"() {
+        when:
+        "".charAt(0)
+
+        then:
+        thrown(IndexOutOfBoundsException)
+    }
+
+    def "メッセージの検証などを行いたい場合は一度変数に受ける"() {
+        when:
+        "".charAt(0)
+
+        then:
+        def e = thrown(IndexOutOfBoundsException)
+        e.message == "String index out of range: 0"
+    }
+
+    def "IDEのサポートを受けやすいように左辺で例外の型を宣言できる"() {
+        when:
+        "".charAt(0)
+
+        then:
+        IndexOutOfBoundsException e = thrown()
+        e.message == "String index out of range: 0"
+    }
+
+    def "特定の例外が発生しないというコンディションを記述できる"() {
+        when:
+        "a".charAt(0)
+
+        then:
+        notThrown(IndexOutOfBoundsException)
+    }
+
+    def "例外が発生しないというコンディションを記述できる"() {
+        when:
+        "a".charAt(0)
+
+        then:
+        noExceptionThrown()
+    }
+}
\ No newline at end of file
diff --git a/src/test/groovy/basic/Basic05_ExpectSpec.groovy b/src/test/groovy/basic/Basic05_ExpectSpec.groovy
new file mode 100644
index 0000000..5529fa1
--- /dev/null
+++ b/src/test/groovy/basic/Basic05_ExpectSpec.groovy
@@ -0,0 +1,11 @@
+package basic
+
+import util.EmbeddedSpec
+
+class Basic05_ExpectSpec extends EmbeddedSpec {
+
+    def "expectの基本"() {
+        expect:
+        Math.max(1, 2) == 2
+    }
+}
\ No newline at end of file
diff --git a/src/test/groovy/basic/Basic06_CleanupSpec.groovy b/src/test/groovy/basic/Basic06_CleanupSpec.groovy
new file mode 100644
index 0000000..5e539ca
--- /dev/null
+++ b/src/test/groovy/basic/Basic06_CleanupSpec.groovy
@@ -0,0 +1,17 @@
+package basic
+
+import util.EmbeddedSpec
+
+class Basic06_CleanupSpec extends EmbeddedSpec {
+
+    def "cleanupの基本"() {
+        when:
+        def file = File.createTempFile("spock", ".txt")
+
+        then:
+        notThrown(IOException)
+
+        cleanup: "ここでお掃除する"
+        file.delete()
+    }
+}
\ No newline at end of file
diff --git a/src/test/groovy/basic/Basic07_FixtureSpec.groovy b/src/test/groovy/basic/Basic07_FixtureSpec.groovy
new file mode 100644
index 0000000..85a949d
--- /dev/null
+++ b/src/test/groovy/basic/Basic07_FixtureSpec.groovy
@@ -0,0 +1,82 @@
+package basic
+
+import spock.lang.Shared
+import spock.lang.Specification
+import spock.util.EmbeddedSpecRunner
+import util.EmbeddedSpec
+
+class Basic07_FixtureSpec extends EmbeddedSpec {
+
+    // フィールドを仕様してフィクスチャを構築できる
+    // フィーチャメソッドの実行毎に初期化される
+    Stack stack = new Stack()
+
+    // フィーチャメソッド間でフィクスチャを共有したい場合は
+    // @Sharedアノテーションを使用する
+    @Shared
+    Stack globalSatck = new Stack()
+
+    // staticを使用しても@Sharedと同じ効果があるが、
+    // 読み手に意図を伝えるためにstaticは定数宣言にのみ使用すること
+    static final PI = 3.141592654
+
+    // スペッククラスの最初に1度実行
+    def setupSpec() {
+    }
+
+    // スペッククラスの最後に1度実行
+    def cleanupSpec() {
+    }
+
+    // すべてのフィーチャーメソッドの前に実行
+    def setup() {
+        log = []
+    }
+
+    // すべてのフィーチャーメソッドの後に実行
+    def cleanup() {
+    }
+
+    def "フィクスチャの初期化順序"() {
+        when:
+        runner.runWithImports """
+            abstract class Base extends Specification {
+              def getLog() { basic.Basic07_FixtureSpec.log }
+
+              def baseField = new Object(){{ basic.Basic07_FixtureSpec.log << "baseField" }}
+              def setup() { log << "baseSetup" }
+              def cleanup() { log << "baseCleanup" }
+              def setupSpec() { log << "baseSetupSpec" }
+              def cleanupSpec() { log << "baseCleanupSpec" }
+            }
+
+            class ChildSpec extends Base {
+              def getLog() { basic.Basic07_FixtureSpec.log }
+
+              def childFiled = new Object(){{ basic.Basic07_FixtureSpec.log << "childField" }}
+              def setup() { log << "childSetup" }
+              def cleanup() { log << "childCleanup" }
+              def setupSpec() { log << "childSetupSpec" }
+              def cleanupSpec() { log << "childCleanupSpec" }
+
+              def feature() { expect: true }
+            }
+        """
+
+        then:
+        log == [
+            "baseSetupSpec",
+            "childSetupSpec",
+            "baseField",
+            "childField",
+            "baseSetup",
+            "childSetup",
+            "childCleanup",
+            "baseCleanup",
+            "childCleanupSpec",
+            "baseCleanupSpec"
+        ]
+    }
+
+    static log
+}
diff --git a/src/test/groovy/datadriventest/DDT01_BasicSpec.groovy b/src/test/groovy/datadriventest/DDT01_BasicSpec.groovy
new file mode 100644
index 0000000..1664da3
--- /dev/null
+++ b/src/test/groovy/datadriventest/DDT01_BasicSpec.groovy
@@ -0,0 +1,65 @@
+package datadriventest
+
+import org.spockframework.compiler.InvalidSpecCompileException
+import spock.lang.Issue
+import spock.lang.Specification
+import spock.util.EmbeddedSpecCompiler
+import util.EmbeddedSpec
+
+class DDT01_BasicSpec extends EmbeddedSpec {
+
+    def "2つの数字の最大値"(int x, int y, int z) {
+        expect:
+        Math.max(x, y) == z
+
+        where: "以下をデータテーブルという"
+        x | y | z // <- 1行目はデータ変数を定義したヘッダ
+        1 | 3 | 3 // <- 2行目以降がデータ列となる
+        7 | 4 | 7
+        0 | 0 | 0
+    }
+
+    def "もう少しシンタックスを改善できる"(/* メソッドの引数は省略可能*/) {
+        expect:
+        Math.max(x, y) == z
+
+        where: "データの区切りに||を使用できる"
+        x | y || z
+        1 | 3 || 3
+        7 | 4 || 7
+        0 | 0 || 0
+    }
+
+    def "データテーブルは必ず2以上のカラムが必要ない場合は_を使用する"() {
+        expect:
+        x > 0
+
+        where:
+        x | _
+        1 | _
+        7 | _
+        8 | _
+        // 値が1つしかない場合はデータパイプを使用したほうがおすすめ
+        // 不要な_はあまり綺麗ではない
+    }
+
+    def "インスタンスフィールドにはアクセス出来ないので注意"() {
+        when:
+        compiler.compileSpecBody """
+            def instanceField
+
+            def foo() {
+              expect:
+              true
+
+              where:
+              x             | y
+              instanceField | 1
+            }
+        """.stripMargin()
+
+        then:
+        def e = thrown(InvalidSpecCompileException)
+        e.message =~ "Only @Shared and static fields may be accessed from here"
+    }
+}
diff --git a/src/test/groovy/datadriventest/DDT02_UnrollSpec.groovy b/src/test/groovy/datadriventest/DDT02_UnrollSpec.groovy
new file mode 100644
index 0000000..b1b4a6c
--- /dev/null
+++ b/src/test/groovy/datadriventest/DDT02_UnrollSpec.groovy
@@ -0,0 +1,66 @@
+package datadriventest
+
+import bank.Account
+import spock.lang.Specification
+import spock.lang.Unroll
+import util.EmbeddedSpec
+
+class DDT02_UnrollSpec extends EmbeddedSpec {
+
+    @Unroll
+    def "Unrollの基本"() {
+        setup:
+        def account = new Account(balance: balance)
+
+        expect:
+        account.empty == empty
+
+        true
+        where:
+        balance | empty
+        0       | true
+        1       | false
+    }
+
+    @Unroll
+    def "balanceが #balance の場合、isEmpty()は #empty"() {
+        setup:
+        def account = new Account(balance: balance)
+
+        expect:
+        account.empty == empty
+
+        true
+        where:
+        balance | empty
+        0       | true
+        1       | false
+    }
+
+    @Unroll("balanceが #balance の場合、isEmpty()は #empty")
+    def "アノテーションの中でも宣言できる"() {
+        setup:
+        def account = new Account(balance: balance)
+
+        expect:
+        account.empty == empty
+
+        true
+        where:
+        balance | empty
+        0       | true
+        1       | false
+    }
+
+    @Unroll("#name.toUpperCase() の長さは #name.length")
+    def "引数なしのメソッド呼び出しとプロパティアクセスが使用可能"() {
+        expect:
+        true
+
+        where:
+        name      | _
+        "Ethan"   | _
+        "Michael" | _
+        "Daniel"  | _
+    }
+}
diff --git a/src/test/groovy/datadriventest/DDT03_DataPipesSpec.groovy b/src/test/groovy/datadriventest/DDT03_DataPipesSpec.groovy
new file mode 100644
index 0000000..bea0934
--- /dev/null
+++ b/src/test/groovy/datadriventest/DDT03_DataPipesSpec.groovy
@@ -0,0 +1,60 @@
+package datadriventest
+
+import groovy.sql.Sql
+import spock.lang.Shared
+import spock.lang.Specification
+import util.EmbeddedSpec
+
+class DDT03_DataPipesSpec extends EmbeddedSpec {
+
+    @Shared
+    def sql
+
+    def setupSpec() {
+        setupDb()
+    }
+
+    def "データパイプの基本"() {
+        expect:
+        Math.max(x, y) == z
+
+        where:
+        // データ変数にデータプロバイダを接続する
+        // データプロバイダにはGroovyでイテレーション可能なオブジェクトが使用出来る
+        // Collection、Stringや、Iterableが使用出来るということ
+        x << [1, 7, 0]
+        y << [3, 4, 0]
+        z << [3, 7, 0]
+
+        // データテーブルで以下を定義した場合と同じ
+        //  x | y || z
+        //  1 | 3 || 3
+        //  7 | 4 || 7
+        //  0 | 0 || 0
+    }
+
+    def "外部リソースをデータプロバイダとして使用する"() {
+        expect:
+        Math.max(x, y) == z
+
+        cleanup:
+        sql.close()
+
+        where:
+        // []を使用するとGroovyのマルチ代入ライクに複数のデータ変数へ接続できる
+        [x, y, z] << sql.rows("SELECT a, b, c FROM maxdata")
+
+        // 使用しない値がある場合は _ で無視することが出来る
+        // [x, _, z] << sql.rows("SELECT a, b, c FROM maxdata")
+    }
+
+    private setupDb() {
+        sql = Sql.newInstance "jdbc:h2:mem:", "org.h2.Driver"
+        sql.execute("CREATE TABLE maxdata (a INTEGER, b INTEGER, c INTEGER)")
+
+        def metadata = sql.dataSet("maxdata")
+        metadata.add (a: 3, b: 5, c: 5)
+        metadata.add (a: 7, b: 0, c: 7)
+        metadata.add (a: 0, b: 0, c: 0)
+    }
+}
diff --git a/src/test/groovy/datadriventest/DDT04_OtherSpec.groovy b/src/test/groovy/datadriventest/DDT04_OtherSpec.groovy
new file mode 100644
index 0000000..f56e898
--- /dev/null
+++ b/src/test/groovy/datadriventest/DDT04_OtherSpec.groovy
@@ -0,0 +1,50 @@
+package datadriventest
+
+import bank.Account
+import spock.lang.Unroll
+import util.EmbeddedSpec
+
+class DDT04_OtherSpec extends EmbeddedSpec {
+
+    def "変数に受けることもできる"() {
+        expect:
+        Math.max(x, y) == z
+
+        where:
+        x = 1
+        y = 3
+        z = 3
+    }
+
+    def "組み合わせて使用する"() {
+        expect:
+        Math.max(x, y) == z
+
+        where:
+        x | _
+        1 | _
+        7 | _
+        0 | _
+
+        y << [3, 4, 0]
+
+        z = x > y ? x : y
+    }
+
+    @Unroll("口座の残高が #balance 円の場合、口座は #emptyMessage")
+    def "Unrollの変数に活用するとより文章らしく書くことも可能"() {
+        setup:
+        def account = new Account(balance: balance)
+
+        expect:
+        account.empty == empty
+
+        true
+        where:
+        balance | empty
+        0       | true
+        1       | false
+
+        emptyMessage = empty ? "空である" : "空ではない"
+    }
+}
\ No newline at end of file
diff --git a/src/test/groovy/mock/Mock01_BasicSpec.groovy b/src/test/groovy/mock/Mock01_BasicSpec.groovy
new file mode 100644
index 0000000..a83aff3
--- /dev/null
+++ b/src/test/groovy/mock/Mock01_BasicSpec.groovy
@@ -0,0 +1,69 @@
+package mock
+
+import util.EmbeddedSpec
+
+class Mock01_BasicSpec extends EmbeddedSpec {
+
+    def publisher = new Publisher()
+
+    def "モックの基本的な使い方"() {
+        setup: "Mockメソッドで作成する"
+        def subscriber1 = Mock(Subscriber)
+
+        and: "左辺で型指定をすることも可能"
+        Subscriber subscriber2 = Mock()
+
+        and: "仕様対象にモックを設定する"
+        publisher.subscribers << subscriber1 << subscriber2
+
+        when:
+        publisher.send("hello")
+
+        then: "subscriberで1回helloを受信すること"
+        1 * subscriber1.receive("hello") // <- コンディションという
+        1 * subscriber2.receive("hello")
+    }
+
+    def "モックのデフォルト動作"() {
+        when:
+        def mock = Mock(Mockable)
+
+        then: "booleanはfalse"
+        mock.getBoolean() == false
+
+        and: "数値のプリミティブ型は0"
+        mock.getByte() == 0
+        mock.getShort() == 0
+        mock.getInt() == 0
+        mock.getLong() == 0
+        mock.getFloat() == 0f
+        mock.getDouble() == 0d
+
+        and: "オブジェクトはnull"
+        mock.getObject() == null
+        mock.getVoid() == null
+        mock.getDynamic() == null
+    }
+
+    interface Mockable {
+        boolean getBoolean()
+
+        byte getByte()
+
+        short getShort()
+
+        int getInt()
+
+        long getLong()
+
+        float getFloat()
+
+        double getDouble()
+
+        Object getObject()
+
+        void getVoid()
+
+        def getDynamic()
+    }
+}
\ No newline at end of file
diff --git a/src/test/groovy/mock/Mock02_MockingSpec.groovy b/src/test/groovy/mock/Mock02_MockingSpec.groovy
new file mode 100644
index 0000000..2937a45
--- /dev/null
+++ b/src/test/groovy/mock/Mock02_MockingSpec.groovy
@@ -0,0 +1,206 @@
+package mock
+
+import util.EmbeddedSpec
+
+class Mock02_MockingSpec extends EmbeddedSpec {
+
+    def publisher = new Publisher()
+    def subscriber = Mock(Subscriber)
+
+    def setup() {
+        publisher.subscribers << subscriber
+    }
+
+    def "インタラクションの各パート"() {
+        when:
+        publisher.send("hello")
+
+        then: "インタラクションは 多重度、対象制約、メソッド制約、引数制約 の4つのパートからなる"
+        1 * subscriber.receive("hello")
+//      |   |          |       |
+//      |   |          |       引数制約(argument constraint)
+//      |   |          メソッド制約(method constraint)
+//      |   対象制約(target constraint)
+//      多重度(cardinality)
+    }
+
+    def "多重度の色々"() {
+        when:
+        // 適当に呼び出す
+        5.times { publisher.send("hello") }
+
+        then:
+        // 多重度はメソッドの呼び出し回数を表す
+        // これは固定の数値、またはGroovyの範囲(Range)が使用できる
+        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を含む)
+    }
+
+    def "対象制約の色々"() {
+        when:
+        // 適当に呼び出す
+        2.times { publisher.send("hello") }
+
+        then:
+        // 対象制約は呼び出し対象となるモックオブジェクトを表す
+        1 * subscriber.receive("hello") // 'subscriber'への呼び出し
+        1 * _.receive("hello")          // 任意のオブジェクトへの呼び出し
+    }
+
+    def "メソッド制約の色々"() {
+        when:
+        3.times { publisher.send("hello") }
+
+        then:
+        // メソッド制約は呼び出し対象となるメソッドを表す
+        // 対象制約とほとんど同じだが/.../で正規表現による指定が可能
+
+        1 * subscriber.receive("hello") // 'receive'のメソッド
+        1 * subscriber._("hello")       // 任意のメソッド
+        1 * subscriber./r.*e/("hello")  // 'r'で始まり'e'で終わるメソッド
+    }
+
+    def "引数制約の色々"() {
+        when:
+        // 適当に呼び出す
+        6.times { publisher.send("hello") }
+        publisher.send("not hello")
+
+        then:
+        1 * subscriber.receive("hello")           // "hello"に一致する
+        1 * subscriber.receive(!"hello")          // "hello"以外
+        0 * subscriber.receive()                  // 引数なし
+        1 * subscriber.receive(_)                 // 任意の引数
+        1 * subscriber.receive(* _)               // 任意の引数リスト
+        1 * subscriber.receive(!null)             // null以外
+        1 * subscriber.receive(_ as String)       // 任意のString
+        1 * subscriber.receive({ it.size() > 3 }) // 引数のsize()が3以上
+    }
+
+    def "ワイルドカード的なインタラクション"() {
+        when:
+        4.times { publisher.send("hello") }
+
+        then:
+        1 * subscriber._(* _)    // subscriberの任意のメソッド、任意の引数の呼び出し
+        1 * subscriber._         // 上記のショートカット
+        1 * _._                  // 任意のモックオブジェクトに対する任意の呼び出し
+        1 * _                    // 上記のショートカット
+    }
+
+    def "Strictモッキング"() {
+        when:
+        publisher.send("hello")
+
+        then:
+        1 * subscriber.receive("hello") // `subscriber`の`receive`メソッドが1回呼ばれる
+
+        //最後に0 * _を定義する点がポイントで、
+        // これにより定義していないメソッド呼び出しがないことを検証できる
+        // (定義していないメソッド呼び出しがないことを確認)
+        0 * _
+    }
+
+    def "呼び出し順序の確認"() {
+        // Spcokはthen:ブロック内で定義されたインタラクションが
+        // 最終的に満たされていれれば正常にインタラクションが行われたものとして扱う
+        when:
+        1.times { publisher.send("goodbye") }
+        2.times { publisher.send("hello") }
+
+        then: "この中は最終的にすべてのインタラクションが満たされていれば呼び出し順序は関係ない"
+        2 * subscriber.receive("hello")
+        1 * subscriber.receive("goodbye")
+
+        // 呼び出し順序の確認を行うにはthenブロックを分割する
+        when:
+        2.times { publisher.send("hello") }
+        1.times { publisher.send("goodbye") }
+
+        then:
+        2 * subscriber.receive("hello")
+
+        then:
+        1 * subscriber.receive("goodbye")
+    }
+
+    def "インタラクションの宣言場所"() {
+        setup:
+        // thenブロック以外でもインタラクションを宣言できる
+        3 * subscriber.receive("hello")
+
+        when:
+        3.times { publisher.send("hello") }
+
+        then:
+        true
+    }
+
+    def "モック生成時のインタクション宣言"() {
+        setup:
+        def subscriber1 = Mock(Subscriber) {
+            1 * receive("hello")
+        }
+        // 型宣言バージョン
+        Subscriber subscriber2 = Mock() {
+            1 * /*対象制約は含まれない*/ receive("hello") // <- クロージャからsubscriberが参照できないため
+        }
+
+        when:
+        subscriber1.receive("hello")
+        subscriber2.receive("hello")
+
+        then:
+        noExceptionThrown()
+    }
+
+    def "インタラクションのグループ化"() {
+        when:
+        1.times { publisher.send("goodbye") }
+        2.times { publisher.send("hello") }
+
+        then:
+        with(subscriber) {
+            2 * receive("hello")
+            1 * receive("goodbye")
+        }
+    }
+
+    def "インタラクションはwhenブロックに移動される"() {
+        // インタラクションは実行時にはwhenブロックの前で評価される
+        // そのため、以下は
+        //
+        //   when:
+        //   publisher.send("hello")
+        //
+        //   then:
+        //   def message = "hello"
+        //   1 * subscriber.receive(message)
+        //
+        // 実行時には以下になる
+        //
+        //   1 * subscriber.receive(message)
+        //
+        //   when:
+        //   publisher.send("hello")
+        //
+        //   then:
+        //   def message = "hello"
+        //
+        // このため、thenブロックで宣言した変数を
+        // インタラクションで参照するようなことをすると
+        // MissingPropertyを実行時に引き起こす
+        when: "これを回避するにはinteractionブロックを使用する"
+        publisher.send("hello")
+
+        then: "interactionを使用するとブロックごとwhenブロックの前に移動する"
+        interaction {
+            def message = "hello"
+            1 * subscriber.receive(message)
+        }
+    }
+}
diff --git a/src/test/groovy/mock/Mock03_StubbingSpec.groovy b/src/test/groovy/mock/Mock03_StubbingSpec.groovy
new file mode 100644
index 0000000..fc30018
--- /dev/null
+++ b/src/test/groovy/mock/Mock03_StubbingSpec.groovy
@@ -0,0 +1,122 @@
+package mock
+
+import util.EmbeddedSpec
+
+class Mock03_StubbingSpec extends EmbeddedSpec {
+
+    def subscriber = Mock(Subscriber)
+
+    def "スタビングの基本"() {
+        setup: "モッキングとの違いはレポンスジェネレータを>>で指定する"
+        subscriber.receive(_) >> "ok"
+//      |          |       |     |
+//      |          |       |     レスポンスジェネレータ
+//      |          |       引数制約(argument constraint)
+//      |          メソッド制約(method constraint)
+//      対象制約(target constraint)
+
+        when:
+        def answer = subscriber.receive("hello")
+
+        then:
+        answer == "ok"
+    }
+
+    def "固定の値を返す"() {
+        setup: "固定の値を定義するには >> を使用する"
+        subscriber.receive("message1") >> "ok"
+        subscriber.receive("message2") >> "fail"
+
+        expect:
+        subscriber.receive("message1") == "ok"
+        subscriber.receive("message2") == "fail"
+    }
+
+    def "呼び出し毎に値を変更する"() {
+        setup: "シーケンシャルな値を定義するには >>> を使用する"
+        subscriber.receive(_) >>> ["ok", "error", "error", "ok"]
+
+        expect: "1回目の呼び出し"
+        subscriber.receive("x") == "ok"
+
+        and: "2、3回目の呼び出し"
+        subscriber.receive("x") == "error"
+        subscriber.receive("x") == "error"
+
+        and: "4回目以降の呼び出し"
+        subscriber.receive("x") == "ok"
+        subscriber.receive("x") == "ok"
+        subscriber.receive("x") == "ok"
+        subscriber.receive("x") == "ok"
+        // ...
+    }
+
+    def "動的に値を返す"() {
+        when: "動的に値を返すにはクロージャを使用する"
+        def subscriber1 = Mock(Subscriber)
+
+        // クロージャの引数を1つ、かつ型の指定なしとすると、引数リストが渡される
+        subscriber1.receive(_) >> { args -> args[0].size() > 3 ? "ok" : "fail" }
+
+        then:
+        subscriber1.receive("xxx") == "fail"
+        subscriber1.receive("xxxx") == "ok"
+
+        when: "型指定ありバージョン"
+        def subscriber2 = Mock(Subscriber)
+
+        // クロージャの引数を1つ以上、かつ型を指定すると、引数が自動的にマップされる
+        subscriber2.receive(_) >> { String message -> message.size() > 3 ? "ok" : "fail" }
+
+        then:
+        subscriber1.receive("xxx") == "fail"
+        subscriber1.receive("xxxx") == "ok"
+    }
+
+    def "例外を発生させる"() {
+        setup: "クロージャを使用すれば任意のコードが実行できる"
+        subscriber.receive(_) >> { throw new RuntimeException("例外をスロー") }
+
+        when:
+        subscriber.receive("hello")
+
+        then:
+        def e = thrown(RuntimeException)
+        e.message == "例外をスロー"
+    }
+
+    def "固定値、シーケンシャル、動的な値の組み合わせ"() {
+        setup:
+        subscriber.receive(_) >>> ["ok", "fail", "ok"] >> { throw new InternalError() } >> "ok"
+
+        expect: "初めに3回の呼び出しにok,fail,okを返す"
+        subscriber.receive("hello") == "ok"
+        subscriber.receive("hello") == "fail"
+        subscriber.receive("hello") == "ok"
+
+        when: "次の呼び出しに例外をスローする"
+        subscriber.receive("hello")
+
+        then:
+        thrown(InternalError)
+
+        expect: "5回目以降はokを返す"
+        subscriber.receive("hello") == "ok"
+        subscriber.receive("hello") == "ok"
+        subscriber.receive("hello") == "ok"
+        subscriber.receive("hello") == "ok"
+    }
+
+    def "モッキングとスタビングの組み合わせ"() {
+        setup:
+        // 必ずモッキングとスタビングは同時に行う必要がある
+        // mockitoのように別々に行うことは出来ない
+        subscriber.receive(_) >> "ok"
+
+        when:
+        def answer = subscriber.receive("hello")
+
+        then:
+        answer == "ok"
+    }
+}
diff --git a/src/test/groovy/mock/Mock04_StubSpec.groovy b/src/test/groovy/mock/Mock04_StubSpec.groovy
new file mode 100644
index 0000000..f082a32
--- /dev/null
+++ b/src/test/groovy/mock/Mock04_StubSpec.groovy
@@ -0,0 +1,229 @@
+package mock
+
+import org.spockframework.mock.MockDetector
+import spock.lang.Unroll
+import util.EmbeddedSpec
+
+class Mock04_StubSpec extends EmbeddedSpec {
+
+    def "Stubの基本"() {
+        when:
+        def subscriber = Stub(Subscriber)
+
+        // Stubではスタビングのみ可能で、モッキングは使用できない
+        // これは機能を制限することで、このモックの役割を明確にする
+        subscriber.receive("hello") >> "ok"
+
+        then:
+        subscriber.receive("hello") == "ok"
+
+        // Stubは可能な限りリアルオブジェクトをデフォルト値として返す
+        subscriber.receive("bye") == ""
+    }
+
+    @Unroll("#methodの値は、mockの場合は#mockExpected、stubの場合は#stubExpected")
+    def "MockとStubのデフォルト値の違い"() {
+        // Stubは
+        //  - プリミティブ型は、プリミティブ型のデフォルト値
+        //  - 非プリミティブ数値（BigDecimalのような）の場合は、ゼロ
+        //  - 数字以外の値は空やダミーオブジェクト
+        // を返す
+        given:
+        def mock = Mock(TestInterface)
+        def stub = Stub(TestInterface)
+
+        expect:
+        mock[method] == mockExpected
+        stub[method] == stubExpected
+
+        where:
+        method    | mockExpected | stubExpected
+        'byte'    | 0            | 0
+        'short'   | 0            | 0
+        'int'     | 0            | 0
+        'long'    | 0            | 0
+        'float'   | 0            | 0
+        'double'  | 0            | 0
+        'boolean' | false        | false
+        'char'    | 0            | 0
+
+        and:
+        'byteWrapper'    | null | 0
+        'shortWrapper'   | null | 0
+        'intWrapper'     | null | 0
+        'longWrapper'    | null | 0
+        'floatWrapper'   | null | 0
+        'doubleWrapper'  | null | 0
+        'booleanWrapper' | null | false
+        'charWrapper'    | null | 0
+
+        and:
+        'bigInteger' | null | BigInteger.ZERO
+        'bigDecimal' | null | BigDecimal.ZERO
+
+        and:
+        'charSequence' | null | ""
+        'string'       | null | ""
+        'GString'      | null | ""
+
+        and:
+        'primitiveArray' | null | [] as int[]
+        'interfaceArray' | null | [] as IPerson[]
+        'classArray'     | null | [] as Person[]
+
+        and:
+        'iterable'   | null | []
+        'collection' | null | []
+        'queue'      | null | []
+        'list'       | null | []
+        'set'        | null | [] as Set
+        'map'        | null | [:]
+        'sortedSet'  | null | [] as Set
+        'sortedMap'  | null | [:]
+    }
+
+    def "インタフェースが戻り値のメソッドの場合はそのインタフェースのstubを返す"() {
+        given:
+        def mock = Mock(TestInterface)
+        def stub = Stub(TestInterface)
+
+        expect: "mockの場合はnull"
+        mock.unknownInterface == null
+
+        and: "stubの場合はそのインタフェースのstubを返す"
+        with(stub.unknownInterface) {
+            new MockDetector().isMock(it)
+            name == ""
+            age == 0
+            children == []
+        }
+    }
+
+    def "デフォルトコンストラクタがあるクラスが戻り値の場合はそのクラスのインスタンスを返す"() {
+        given:
+        def mock = Mock(TestInterface)
+        def stub = Stub(TestInterface)
+
+        expect: "mockの場合はnull"
+        mock.unknownClassWithDefaultCtor == null
+
+        and: "stubの場合はそのクラスの本物(モックではない)のインスタンスを返す"
+        with(stub.unknownClassWithDefaultCtor) {
+            !new MockDetector().isMock(it)
+            name == "default"
+            age == 0
+            children == null
+        }
+    }
+
+    def "デフォルトコンストラクタがないクラスが戻り値の場合はそのクラスのstubを返す"() {
+        given:
+        def mock = Mock(TestInterface)
+        def stub = Stub(TestInterface)
+
+        expect: "mockの場合はnull"
+        mock.unknownClassWithoutDefaultCtor == null
+
+        and: "stubの場合はそのクラスのstubを返す"
+        with(stub.unknownClassWithoutDefaultCtor) {
+            new MockDetector().isMock(it)
+            name == ""
+            age == 0
+            children == []
+        }
+    }
+
+    static interface TestInterface {
+        byte getByte()
+
+        short getShort()
+
+        int getInt()
+
+        long getLong()
+
+        float getFloat()
+
+        double getDouble()
+
+        boolean getBoolean()
+
+        char getChar()
+
+        Byte getByteWrapper()
+
+        Short getShortWrapper()
+
+        Integer getIntWrapper()
+
+        Long getLongWrapper()
+
+        Float getFloatWrapper()
+
+        Double getDoubleWrapper()
+
+        Boolean getBooleanWrapper()
+
+        Character getCharWrapper()
+
+        BigInteger getBigInteger()
+
+        BigDecimal getBigDecimal()
+
+        CharSequence getCharSequence()
+
+        String getString()
+
+        GString getGString()
+
+        int[] getPrimitiveArray()
+
+        IPerson[] getInterfaceArray()
+
+        Person[] getClassArray()
+
+        Iterable getIterable()
+
+        Collection getCollection()
+
+        Queue getQueue()
+
+        List getList()
+
+        Set getSet()
+
+        Map getMap()
+
+        SortedSet getSortedSet()
+
+        SortedMap getSortedMap()
+
+        IPerson getUnknownInterface()
+
+        Person getUnknownClassWithDefaultCtor()
+
+        ImmutablePerson getUnknownClassWithoutDefaultCtor()
+    }
+
+    static interface IPerson {
+        String getName()
+
+        int getAge()
+
+        List<String> getChildren()
+    }
+
+    static class Person implements IPerson {
+        String name = "default"
+        int age
+        List<String> children
+    }
+
+    static class ImmutablePerson extends Person {
+        ImmutablePerson(String name, int age, List<String> children) {
+            this.name = name
+            this.age = age
+            this.children = children
+        }
+    }
+}
diff --git a/src/test/groovy/mock/Mock05_SpySpec.groovy b/src/test/groovy/mock/Mock05_SpySpec.groovy
new file mode 100644
index 0000000..bd31750
--- /dev/null
+++ b/src/test/groovy/mock/Mock05_SpySpec.groovy
@@ -0,0 +1,81 @@
+package mock
+
+import bank.Account
+import util.EmbeddedSpec
+
+class Mock05_SpySpec extends EmbeddedSpec {
+
+    def "Spyの基本"() {
+        setup: "Spyを使用して、クラスとコンストラクタの引数を指定する"
+        def account = Spy(Account, constructorArgs: [1000])
+
+        when: "メソッド呼び出しは本物オブジェクトに委譲される"
+        account.credit(500)
+
+        then:
+        account.balance == 1500
+
+        and: "インタラクションが定義できる"
+        1 * account.credit(_)
+    }
+
+    def "constructorArgsを指定しない場合はデフォルトコンストラクタが使用される"() {
+        setup:
+        def account = Spy(Account)
+
+        when:
+        account.credit(500)
+
+        then:
+        account.balance == 500
+    }
+
+    def "スタビングを行うと本物オブジェクトを呼ばない"() {
+        setup:
+        def account = Spy(Account)
+
+        and: "スタビング"
+        account.credit(_) >> 0
+
+        when: "メソッド呼び出しは本物オブジェクトを呼ばない"
+        account.credit(500)
+
+        then:
+        account.balance == 0
+    }
+
+    def "スタビングしつつ本物オブジェクトを呼び出す"() {
+        setup:
+        def account = Spy(Account)
+
+        when: "callRealMethodを使用した場合"
+        account.credit(_) >> { BigDecimal amount ->
+            // callRealMethodで本物オブジェクトのメソッドが呼ばれる
+            // 引数は自動的に設定されることに注意(明示的に指定する必要がない)
+            callRealMethod()
+            return 123
+        }
+
+        and:
+        def result = account.credit(500)
+
+        then:
+        result == 123          // スタビングした戻り値
+        account.balance == 500 // 本物オブジェクトの処理が行われている
+
+        when: "callRealMethodWithArgsを使用した場合"
+        account.debit(_) >> { BigDecimal amount ->
+            // callRealMethodWithArgsを使用するとメソッド呼び出しの
+            // 引数がコントロールできる
+            callRealMethodWithArgs(300 as BigDecimal)
+            return 321
+        }
+
+        and:
+        result = account.debit(500)
+
+        then:
+        result == 321
+        account.balance == 200
+    }
+}
diff --git a/src/test/groovy/mock/Mock06_GroovyMockSpec.groovy b/src/test/groovy/mock/Mock06_GroovyMockSpec.groovy
new file mode 100644
index 0000000..3eecbde
--- /dev/null
+++ b/src/test/groovy/mock/Mock06_GroovyMockSpec.groovy
@@ -0,0 +1,72 @@
+package mock
+
+import util.EmbeddedSpec
+
+/**
+ * Groovyモック
+ * <p>
+ * これまで説明してきたモック機能は、呼び出し元のコードがJava・Groovyで動作する。
+ * Groovyモックは、呼び出し元がGroovyの場合に、Groovy特有のテスト機能をサポートしています。
+ * <p>
+ * Groovyモックは{@link spock.lang.MockingApi#GroovyMock()}、{@link spock.lang.MockingApi#GroovyStub()}、
+ * {@link spock.lang.MockingApi#GroovySpy()}メソッドで作成します。
+ */
+class Mock06_GroovyMockSpec extends EmbeddedSpec {
+
+    def "動的メソッドのモック"() {
+        setup:
+        def person = GroovyMock(Person)
+
+        when: "動的メソッドの呼び出し"
+        person.say("hello")
+
+        then: "動的メソッドのモッキング"
+        1 * person.say("hello")
+    }
+
+    def "グローバルモックの基本"() {
+        setup:
+        // 変数で受けたglobalSpyはモッキング、スタビングを定義するためだけに使用する
+        // このオブジェクトをコラボレータとして差し替えたりはしない
+        def globalSpy = GroovySpy(Person, global: true)
+
+        when:
+        new Person(name: "Graeme").name
+        new Person(name: "Burt").name
+
+        then:
+        2 * globalSpy.name
+    }
+
+    def "コンストラクタのスタビング"() {
+        setup:
+        GroovySpy(Person, global: true)
+
+        and: "コンストラクタをスタビング"
+        new Person(_) >> new Person(name: "mock")
+
+        expect:
+        new Person(name: "Graeme").name == "mock"
+        new Person(name: "Burt").name == "mock"
+    }
+
+    def "staticメソッドのモッキング"() {
+        setup:
+        GroovySpy(Collections, global: true)
+
+        when:
+        Collections.emptyList()
+        Collections.nCopies(42, "elem")
+
+        then:
+        1 * Collections.emptyList()
+        1 * Collections.nCopies(42, "elem")
+        0 * _
+    }
+
+    static class Person {
+        String name
+
+
+    }
+}
\ No newline at end of file
diff --git a/src/test/groovy/util/EmbeddedSpec.groovy b/src/test/groovy/util/EmbeddedSpec.groovy
new file mode 100644
index 0000000..5cc07af
--- /dev/null
+++ b/src/test/groovy/util/EmbeddedSpec.groovy
@@ -0,0 +1,16 @@
+package util
+
+import spock.lang.Shared
+import spock.lang.Specification
+import spock.util.EmbeddedSpecCompiler
+import spock.util.EmbeddedSpecRunner
+
+abstract class EmbeddedSpec extends Specification {
+
+    @Shared
+    EmbeddedSpecCompiler compiler = new EmbeddedSpecCompiler()
+
+    @Shared
+    EmbeddedSpecRunner runner = new EmbeddedSpecRunner()
+
+}
\ No newline at end of file
now