SpockのMockingApiで値をキャプチャする方法

# 概要

SpockのUnitTestで、Mockオブジェクトの引数をキャプチャする。
Mockitoでいうところの下記をSpockのMockingApiでやる。

```java
Mockito.verify(hugaRepository).save(args.capture())
```

# サンプル

HogeService と FugaRepository があるとする。
HogeServiceはビジネスロジックを担い、FugaRepositoryはDB更新を行う。

HogeService がFugaRepository を呼び出しており、FugaRepositoryをMock化しているようなケースを想定する。

```java
public class HogeService {
  private FugaRepository fugaRepository;

  public HogeService(FugaRepository fugaRepository) {
    this.fugaRepository = fugaRepository;
  }

  // テスト対象メソッド
  public void update(Sample sample) {
    // do Something
    sample.value = "更新したい"

    // DB操作はMockにしたい
    fugaRepository.update(sample)

    // do Something
  }

}
```

```java
public interface FugaRepository {
  // データベースへの更新処理
  int update(Sample sample);
}
```

## captureしない場合

```groovy
@Unroll
class HogeServiceSpec extends Specification {
  HogeService hogeService
  FugaRepository fugaRepository

  def setup() {
  fugaRepository = Mock()
    hogeService = new HogeService(fugaRepository)
  }

  def "メソッドの呼び出しだけテスト"() {
    setup:
    // mockは常にスタブ値を返す
    fugaRepository.update(*_) >> 1
    def sample = new Sample

    when:
    hogeService.update(sample)

    then:
    // 呼び出されているかどうかだけテスト
    1 * fugaRepository.update(_*)
  }
}
```

## 一度だけcaptureする場合

Closure composition を使う。

```groovy
@Unroll
class HogeServiceSpec extends Specification {
  HogeService hogeService
  FugaRepository fugaRepository

  def setup() {
    fugaRepository = Mock()
    hogeService = new HogeService(fugaRepository)
  }

  def "メソッドの呼び出しだけテスト"() {
    setup:
    def sample = new Sample
    // キャプチャ保持用の変数はwhenブロックより前に宣言の必要がある
    def captured

    when:
    hogeService.update(sample)

    then:
    // captureする
    1 * fugaRepository.update(*_) >> { sampleArg ->
      // Closureのなかで参照可能
      sampleArg.value == "更新したい"
      captured = sample

      // スタブ値を返す
      return 1
    }

    // closureの外でも参照可能
    captured.value == "更新したい"
  }
}
```

## 複数回captureする場合

仮にHogeServiceが複数回fugaRepository#update()を呼んでいた場合に、それぞれをcaptureする。
やり方は同じ。

```groovy
@Unroll
class HogeServiceSpec extends Specification {
  HogeService hogeService
  FugaRepository fugaRepository

  def setup() {
  fugaRepository = Mock()
    hogeService = new HogeService(fugaRepository)
  }

  def "メソッドの呼び出しだけテスト"() {
    setup:
    def sample = new Sample
    // キャプチャ保持用の変数はwhenブロックより前に宣言の必要がある
    def capturedStock = []

    when:
    hogeService.update(sample)

    then:
    // 複数回Mockが呼ばれるようなケースを想定
    (0..4).each {
    1 * fugaRepository.update(*_) >> { sampleArg -> {
      capturedStock.add(sampleArg)

      // スタブ値を返す
      return 1
    }
    // closureの外でも参照可能
    capturedStock.size() == 5
  }
}
```

## 補足

複数回呼び出しをワザワザ書いたのは、自分がClosure compositionを理解しておらずちょっとハマったから。
CaptureするClosureをCompositした場合、Mockのスタブ値が必要ならreturnしてあげないといけない。

もちろんメソッドのインターフェース次第ではあるけれども、スタブ値返さずに複数回呼び出したら動かなくなった。

# 参考

http://spockframework.org/spock/docs/1.0/interaction_based_testing.html#_mocking
http://www.mscharhag.com/groovy/closure-composition-in-groovy
http://stackoverflow.com/questions/22111212/how-to-do-argument-capture-with-spock-framework
http://stackoverflow.com/questions/16331230/is-there-any-way-to-do-mock-argument-capturing-in-spock