2016-05-17 50 views
0

私はSpockを取得して、単一のbyte[]をパラメータとして受け取るメソッドを疑似しています。Spock:1バイトの[]パラメータを受け入れるメソッドをモックする方法は?

私の製品コードと同じ方法で失敗している単純な玩具例は、以下:

import java.util.function.Consumer 
import spock.lang.Specification 

class ConsumerSpec extends Specification { 
    // ... elided ... 

    def '4: parameter is of an array type using single typed argument'() { 
     given: 
     def consumer = Mock(Consumer) 

     when: 
     consumer.accept([20, 21] as byte[]) 

     then: 
     consumer.accept(_) >> { byte[] arg -> 
      assert arg[0] == 20 
      assert arg[1] == 21 
     } 
    } 

    // ... elided ... 
} 

失敗メッセージ私はスポックのドキュメントに記載された動作に依存し

ConsumerSpec > 4: parameter is of an array type using single typed argument FAILED 
    org.codehaus.groovy.runtime.typehandling.GroovyCastException: Cannot cast object '[[[email protected]]' with class 'java.util.Arrays$ArrayList' to class 'java.lang.Byte' 
     at groovy.lang.Closure.call(Closure.java:423) 
     at org.spockframework.mock.response.CodeResponseGenerator.invokeClosure(CodeResponseGenerator.java:53) 
     at org.spockframework.mock.response.CodeResponseGenerator.doRespond(CodeResponseGenerator.java:36) 
     at org.spockframework.mock.response.SingleResponseGenerator.respond(SingleResponseGenerator.java:31) 
     at org.spockframework.mock.response.ResponseGeneratorChain.respond(ResponseGeneratorChain.java:45) 
     at org.spockframework.mock.runtime.MockInteraction.accept(MockInteraction.java:76) 
     at org.spockframework.mock.runtime.MockInteractionDecorator.accept(MockInteractionDecorator.java:46) 
     at org.spockframework.mock.runtime.InteractionScope$1.accept(InteractionScope.java:41) 
     at org.spockframework.mock.runtime.MockController.handle(MockController.java:39) 
     at org.spockframework.mock.runtime.JavaMockInterceptor.intercept(JavaMockInterceptor.java:72) 
     at org.spockframework.mock.runtime.DynamicProxyMockInterceptorAdapter.invoke(DynamicProxyMockInterceptorAdapter.java:28) 
     at ConsumerSpec.4: parameter is of an array type using single typed argument(ConsumerSpec.groovy:52) 

あります相互作用に基づくテストの下でComputing Return Valuesの場合クロージャは、単一のタイプなしパラメーターを宣言した場合、クロージャは、複数のパラメータまたは単一型指定されたパラメータが宣言している場合、それは... をメソッドの引数リストを渡される、

:私は選択下記の関連ビットを転写しましたメソッドの引数は、パラメータを閉じるために1つずつマップされます。

私はこれらのステートメントを理解しているかどうかを確認するために上記の仕様にいくつかのテストを追加しました。完全MCVEは次のとおりです。

$のGradle --version

------------------------------------------------------------ 
Gradle 2.13 
------------------------------------------------------------ 

Build time: 2016-04-25 04:10:10 UTC 
Build number: none 
Revision:  3b427b1481e7303c90be7b05079b05b1c 

Groovy:  2.4.4 
Ant:   Apache Ant(TM) version 1.9.6 compiled on June 29 2015 
JVM:   1.8.0_91 (Oracle Corporation 25.91-b14) 
OS:   Linux 4.4.8-300.fc23.x86_64 amd64 

// build.gradle

plugins { 
    id 'groovy' 
} 

repositories { 
    mavenCentral() 
} 

dependencies { 
    testCompile(
    [group: 'org.spockframework', name: 'spock-core', version: '1.0-groovy-2.4'] 
) 
} 

// SRC /テスト/グルーヴィー/ ConsumerSpec.groovy

import java.util.function.Consumer 
import spock.lang.Specification 

class ConsumerSpec extends Specification { 
    def '1: parameter is of a non-array type using single untyped argument'() { 
     given: 
     def consumer = Mock(Consumer) 

     when: 
     consumer.accept('value') 

     then: 
     consumer.accept(_) >> { args -> 
      String arg = args[0] 
      assert arg == 'value' 
     } 
    } 

    def '2: parameter is of a non-array type using single typed argument'() { 
     given: 
     def consumer = Mock(Consumer) 

     when: 
     consumer.accept('value') 

     then: 
     consumer.accept(_) >> { String arg -> 
      assert arg == 'value' 
     } 
    } 

    def '3: parameter is of an array type using single untyped argument'() { 
     given: 
     def consumer = Mock(Consumer) 

     when: 
     consumer.accept([20, 21] as byte[]) 

     then: 
     consumer.accept(_) >> { args -> 
      byte[] arg = args[0] 
      assert arg[0] == 20 
      assert arg[1] == 21 
     } 
    } 

    def '4: parameter is of an array type using single typed argument'() { 
     given: 
     def consumer = Mock(Consumer) 

     when: 
     consumer.accept([20, 21] as byte[]) 

     then: 
     consumer.accept(_) >> { byte[] arg -> 
      assert arg[0] == 20 
      assert arg[1] == 21 
     } 
    } 

    def '5: parameter is of an array type without using Mock'() { 
     given: 
     def consumer = { byte[] arg -> 
      assert arg[0] == 20 
      assert arg[1] == 21 
     } as Consumer<byte[]> 

     expect: 
     consumer.accept([20, 21] as byte[]) 
    } 
} 

もう一度、失敗する唯一のテストは(4)です。

SpockやGroovyは、模擬したメソッドをvarargsメソッドのByteで扱いたいと思っていて、byte[]引数を展開しているようです。唯一報告されている唯一の問題は、私の問題のように聞こえる、GROOVY-4843です。これは組み込みのGroovy Mockingフレームワークに対して提出されたもので、決議はありませんでした。

test(4)を期待どおりに動作させる方法はありますか?つまり、1つのパラメータを閉じて型付きの配列引数を使用できるようにするには?または、私はform(3)を使用してスタックしていて、型指定されていないクロージャ引数から実際のメソッド引数を抽出する必要がありますか?

答えて

1

短い答え:それはバグであるため、通常の方法はありません。ハックのみ&トリック。

ここで説明するとおり、クロージャはCodeResponseGenerator :: invokeClosureで呼び出されます。

private Object invokeClosure(IMockInvocation invocation) { 
    Class<?>[] paramTypes = code.getParameterTypes(); 
    if (paramTypes.length == 1 && paramTypes[0] == IMockInvocation.class) { 
     return GroovyRuntimeUtil.invokeClosure(code, invocation); 
    } 

    code.setDelegate(invocation); 
    code.setResolveStrategy(Closure.DELEGATE_FIRST); 
    return GroovyRuntimeUtil.invokeClosure(code, invocation.getArguments()); 
    } 

invocation.getArgumentsは引数のリストを返します。

public static <T> T invokeClosure(Closure<T> closure, Object... args) 

invokeClosureは、引数のリストを取得したときにリストを配列でラップするように、varargsを想定しています。したがって、SpockはObject [] {ArrayList [byte []]}をクロージャに渡します。一方、クロージャは、(あなたがbyte []を宣言するときに)varargsを受け入れることを知っているので、Object [{byte []}]が渡されることを期待しています。ここで例外が発生します。私はそれがバグだと思うし、スポークはすべてのパラメータをJavaMockInterceptor :: interceptの配列でラップする必要はありません。

PS:もう一つ面白いバグ

この1つは罰金

def test() { 
    given: 
    Consumer<Set<Integer>> consumer = Mock() 
    when: 
    consumer.accept([1,2,3] as Set) 
    then: 
    consumer.accept(_) >> { Set<Integer> integer -> 
     assert integer.size() == 3 
    } 
} 

さんが一覧

def test() { 
    given: 
    Consumer<List<Integer>> consumer = Mock() 
    when: 
    consumer.accept([1,2,3]) 
    then: 
    consumer.accept(_) >> { List<Integer> integer -> 
     assert integer.size() == 3 
    } 
} 

で設定を置き換えてみましょう、我々はあなたが見ることができる

integer.size() == 3 
|  |  | 
|  1  false 
[[1, 2, 3]] 

を得るに動作します代わりにリスト<整数>私たちはリストを取得<リスト<整数>>。 なぜそれが起こったのかを理解するには、渡された引数に戻ってみましょう。 この場合、Object [] {ArrayList [ArrayList]}のようになります。 Closureは入力引数がリストであることを知っているため、最初に見つかったものを使用します。

+0

この調査を行う時間をとってくれてありがとう。それは基本的にGROOVY-4843と同じ問題であるようです。私はSpockの歴史に精通していませんが、元のGroovyのテスト/モックフレームワークから分岐してこの問題を継承しているのだろうかと思います。 @PeterNiederwieserのための良い質問でしょう... –

関連する問題