Spock框架Mock签名与文档不符及IntelliJ兼容性问题问询
针对你提出的两个问题,我来逐一拆解解答:
问题1:为何文档中的参数顺序(类在前,命名参数在后)代码能正常运行?
这是Groovy的语法糖特性加上Spock的方法重载设计共同实现的。
Spock为Spy方法提供了多个重载版本,其中就包含一个最后一个参数为Map的重载(比如Spy(Class<T> type, Map<String, Object> options))。而Groovy有个非常实用的语法糖:当方法的最后一个参数是Map时,你不需要显式创建Map对象,直接用key: value的命名参数形式写在参数列表里就行,Groovy会自动把这些命名参数打包成Map,传递给方法的最后一个参数。
所以你写的Spy(SubscriberImpl, constructorArgs: ["Fred"]),在Groovy编译时会被自动转换成Spy(SubscriberImpl, [constructorArgs: ["Fred"]]),刚好匹配到对应的重载方法,因此代码能正常运行。本质上是Groovy帮你做了参数的自动转换,让写法更简洁直观。
问题2:IntelliJ升级后类型识别失效,调整参数顺序后恢复的原因?
在IntelliJ 2017.3版本中,Spock插件或者Groovy的类型推断引擎对这种语法糖做了特殊兼容处理:它能识别出这种命名参数写法对应的是Spock的Spy(Class, Map)重载,从而正确推断出返回值的类型是SubscriberImpl。
升级到2018.1后,IntelliJ的Groovy类型推断逻辑做了优化,变得更严格了——对于这种“非标准”参数顺序的写法(不符合Spy(Map, Class)的显式方法签名),IDE无法再准确匹配到对应的重载方法,导致类型推断失败,也就识别不出subscriber的具体类型了。
而当你调整为Spy([constructorArgs: ["Fred"]] as Map<String, Object>, SubscriberImpl)时,代码完全符合spock.mock.MockingApi中定义的Spy(Map<String, Object> options, Class<T> type)方法签名,IDE能精准匹配到这个方法,自然就能正确推断出返回类型是SubscriberImpl了。
内容的提问来源于stack exchange,提问作者poseidon

