Java
Groovy
spock

Spock で一部のメソッドだけ Mock する

More than 1 year has passed since last update.

この記事は G*Advent Calendar Advent Calendar 2016 の25日目です。
前日は @hikariru さんの GrailsでHTTPクライアントを利用する(もちろんテストも書く) でした。

G*は、半年ほど前からSpockを触り始めただけですが、最終日が空いていたので思い切って投稿します。
歴は浅いものの、Groovy/Spockの柔軟性がテストで威力を発揮するのを実感しています。

概要

Spockで一部のメソッドだけMockする方法について書きます。

テスト対象メソッドが同一オブジェクトの別メソッドを呼んでいる場合などに、対象外のメソッドのみMockしたい場合などを想定しています。
※StubやMockではオブジェクト全体がMock化されます

なお、テスト対象クラスはJavaで書かれています。

テスト対象クラス

public class Name {

  private String firstName;
  private String lastName;

  public Name(String firstName, String lastName) {
    this.firstName = firstName;
    this.lastName = lastName;
  }

  public String getFullName() {
    return getFirstName() + " " + getLastName();
  }

  public String getFirstName() {
    return firstName;
  }

  public String getLastName() {
    return lastName;
  }
}

一部メソッドのMockにはSpyを使おう

Spyを使うことで、一部のメソッドだけ置き換えられます。

class NameSpec extends Specification {
    def "Spyしてみる"() {
        setup:
        // コンストラクタ引数を渡してSpyする
        def spiedName = Spy(Name, constructorArgs: ['Taro', 'Yamada'])
        // 一部のメソッドだけ書き換える
        spiedName.getFirstName() >> 'Hanako'

        expect:
        // 書き換えたメソッド
        spiedName.getFirstName() == 'Hanako'
        // 書き換えていないメソッドはそのまま動く
        spiedName.getLastName() == 'Yamada'
        // 内部的に呼ばれているメソッドも同等に動く
        spiedName.getFullName() == 'Hanako Yamada'
    }
}

MockやStubとの使い分け

参考までにMockやStubの場合も確認しておきます。

Mockの場合

Mockはオブジェクト全体がMockされます。
Mockのふるまいは(必要なら)すべて明示的にする必要があります。

def "Mockしてみる" () {
    setup:
    def mockedName = Mock(Name)
    // 一部のメソッドだけ書き換える
    mockedName.getFirstName() >> 'Taro'

    expect:
    // 書き換えたメソッド
    mockedName.getFirstName() == 'Taro'
    // 書き換えていないメソッドは何もしない
    mockedName.getLastName() == null
    // 内部的に呼ばれているメソッドも何もしない
    mockedName.getFullName() == null
}

Stubの場合

Stubはオブジェクト全体がStubになります。
Stub値(戻り値)を書き換えることができます。
書き換えてないメソッドは空文字やdummyオブジェクトなど適当な値を返します。

def "Stubしてみる"() {
    setup:
    def stubbedName = Stub(Name)
    // 一部のメソッドだけ書き換える
    stubbedName.getFirstName() >> 'Taro'

    expect:
    // 書き換えたメソッド
    stubbedName.getFirstName() == 'Taro'
    // 書き換えていないメソッドは適当な値を返す
    stubbedName.getLastName() == ''
    // 内部的に呼ばれているメソッドは無視して適当な値を返す
    stubbedName.getFullName() == ''
}

なお、Stubの場合MockやSpyで可能な呼び出し回数のテストを実行すると例外が発生するのでご注意下さい。

// InvalidSpecException がthrowされる
1 * stubbedName.getFirstName()

まとめ

  • 一部のメソッドの書き換えには Spy を使う
  • MockやStubとうまく使い分ける
  • Spockは楽しい

参考