首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >史波克:如何模拟接受单个byte[]参数的方法?

史波克:如何模拟接受单个byte[]参数的方法?
EN

Stack Overflow用户
提问于 2016-05-17 02:40:04
回答 1查看 3.7K关注 0票数 0

让Spock模拟接受单个byte[]作为参数的方法有问题。

下面是一个简单的玩具示例,其失败方式与我的生产代码相同:

代码语言:javascript
运行
复制
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 ...
}

失败消息是

代码语言:javascript
运行
复制
ConsumerSpec > 4: parameter is of an array type using single typed argument FAILED
    org.codehaus.groovy.runtime.typehandling.GroovyCastException: Cannot cast object '[[B@43562196]' 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)

在基于交互的测试中,我依赖斯波克文档中描述的计算返回值行为。我有选择地转录了下面的相关内容:

如果闭包声明了一个非类型化参数,它将被传递给该方法的参数列表.如果闭包声明了多个参数或一个类型的参数,则方法参数将逐个映射到闭包参数.

我在上面的规范中添加了一些其他测试,以确定我是否理解这些语句。完整的MCVE如下:

$ gradle --版本

代码语言:javascript
运行
复制
------------------------------------------------------------
Gradle 2.13
------------------------------------------------------------

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

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

代码语言:javascript
运行
复制
plugins {
  id 'groovy'
}

repositories {
  mavenCentral()
}

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

// src/test/groovy/ConsumerSpec.groovy

代码语言:javascript
运行
复制
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希望将模拟的方法视为Byte的varargs方法一样,并且正在解压byte[]参数。我唯一报告的问题听起来有点像我的问题,那就是GROOVY-4843,它是针对内置的Groovy模拟框架提交的,没有解决方案。

是否有一种方法可以使测试(4)按预期的方式运行?也就是说,能够在一个参数的闭包中使用类型化数组参数吗?还是我被困在使用form (3)并且必须从非类型化闭包参数中提取实际的方法参数?

EN

Stack Overflow用户

回答已采纳

发布于 2016-05-21 15:39:28

简单的回答是:没有正常的方法去做,因为这是一个错误。只有黑客和诡计。

解释如下:您的闭包在CodeResponseGenerator::invokeClosure中被调用。

代码语言:javascript
运行
复制
  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返回一个参数列表。

代码语言:javascript
运行
复制
public static <T> T invokeClosure(Closure<T> closure, Object... args)

invokeClosure需要varargs,所以当它获得参数列表时,它会用数组包装这个列表。因此,史波克将Object[] { ArrayList [ byte[] ]}传递给闭包。同时,闭包知道它接受varargs (正如您声明byte[]),因此它期望传递对象[ {byte[]} ]。这里我们得到了例外。我相信这是一个bug,spock不需要用JavaMockInterceptor::intercept::intercept中的数组包装所有参数。

PS:又一个有趣的小虫子

这个很好

代码语言:javascript
运行
复制
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
    }
}

让我们用列表替换Set

代码语言:javascript
运行
复制
def test() {
    given:
    Consumer<List<Integer>> consumer = Mock()
    when:
    consumer.accept([1,2,3])
    then:
    consumer.accept(_) >> { List<Integer> integer ->
        assert integer.size() == 3
    }
}

我们就会得到

代码语言:javascript
运行
复制
integer.size() == 3
|       |      |
|       1      false
[[1, 2, 3]]

您可以看到,我们得到的不是List< Integer>,而是List< List< Integer>>。为了理解为什么会发生这种情况,让我们回到传递的参数。在本例中,它看起来类似于Object[] { ArrayList ArrayList }。闭包知道输入参数是一个列表,所以它需要找到并使用它的第一个参数。

票数 1
EN
查看全部 1 条回答
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/37266235

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档