Spock0.7で追加されたStubについて
Spock0.7でStubを作る機能が追加されました。
http://docs.spockframework.org/en/latest/interaction_based_testing.html#stubs
Mockとの違いはデフォルトで返す値が違うとのこと。mockはnullを返しますがstubでは
といった感じ。どんな値を返すかはorg.spockframework.mock.EmptyOrDummyResponseで定義されている。すごく小さいクラスなので、ざっとみるだけでもどんな動作をするのかわかると思う。
あとorg.spockframework.smoke.mock.StubDefaultResponses.groovyというテストクラスがあるので、それをみるとわかりやすい。
このStubDefaultResponses.groovyをもとにmockとの動作の違いを確認してみた。
package org.yamkazu import org.spockframework.mock.MockDetector import spock.lang.Specification import spock.lang.Unroll class MockStubDiffSpec extends Specification { @Unroll def "#methodの値は、mockの場合は#mockExpected、stubの場合は#stubExpected"() { 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 'byteWrapper' | null | 0 'shortWrapper' | null | 0 'intWrapper' | null | 0 'longWrapper' | null | 0 'floatWrapper' | null | 0 'doubleWrapper' | null | 0 'booleanWrapper' | null | false 'charWrapper' | null | 0 'bigInteger' | null | BigInteger.ZERO 'bigDecimal' | null | BigDecimal.ZERO 'charSequence' | null | "" 'string' | null | "" 'GString' | null | "" 'primitiveArray' | null | [] as int[] 'interfaceArray' | null | [] as IPerson[] 'classArray' | null | [] as Person[] '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) { // 0.7から出来るwithという書き方 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 } } // デフォルトコンストラクタがないクラスのstubを作るのに以下が必要となる。 // "cglib:cglib-nodep:2.2.2" // "org.objenesis:objenesis:1.2" 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 } } }
わかれば意外と素直な動作。