groovy – 使用metaClass的Mock Gradle project.exec {…}
作为测试Gradle插件的一部分,我想要一个groovy方法:project.exec {…}.这是为了确认它正在进行正确的命令行调用.我正在尝试使用元编程:
Project proj = ProjectBuilder.builder().build() proj.metaClass.exec = { Closure obj -> println 'MOCK EXEC' } proj.exec { executable 'echo' args 'PROJECT EXEC' } // prints 'PROJECT EXEC' instead of the 'MOCK EXEC' I expected 令人好奇的是,如果我将两个exec方法重命名为othername,那么它可以正常工作: Project proj = ProjectBuilder.builder().build() proj.metaClass.othername = { Closure obj -> println 'MOCK EXEC' } proj.othername { executable 'echo' args 'PROJECT EXEC' } // prints 'MOCK EXEC' as expected 我试图找出为什么现有的project.exec方法导致元编程失败以及是否有解决方法.请注意,Project是一个接口,但我正在模拟DefaultProject类型的特定实例. 用于截断单个方法的元编程方法来自这个答案:https://stackoverflow.com/a/23818476/1509221 解决方法
在Groovy中,使用metaClass替换接口中定义的方法已被破坏.在这种情况下,exec方法在Project类中定义,它是一个接口.从
GROOVY-3493(最初在2009年报道):
"Cannot override methods via metaclass that are part of an interface implementation" 替代方法 invokeMethod拦截所有方法并且可以工作.这有点矫枉过正,但确实有效.当方法名称与exec匹配时,它会将调用转移到mySpecialInstance对象.否则它将传递给委托,即现有方法.感谢invokeMethod delegation和Logging All Methods对此的输入. // This intercepts all methods,stubbing out exec and passing through all other invokes this.project.metaClass.invokeMethod = { String name,args -> if (name == 'exec') { // Call special instance to track verifications mySpecialInstance.exec((Closure) args.first()) } else { // This calls the delegate without causing infinite recursion MetaMethod metaMethod = delegate.class.metaClass.getMetaMethod(name,args) return metaMethod?.invoke(delegate,args) } } 这很有效,除了你可能会看到关于“错误的参数数量”或“无法在null对象上调用方法xxxxx”的异常.问题是上面的代码不处理方法参数的强制转换.对于project.files(Object … paths),invokeMethod的args应该是[[‘path1′,’path2’]]的形式.但是,在某些情况下,调用文件(null)或文件(),因此invokeMethod的args分别为[null]和[],因为它预期[[]]失败.产生上述错误. 以下代码仅解决了文件方法的问题,但这对我的单元测试来说已经足够了.我仍然想找到一种更好的强制类型或更换单一方法的方法. // As above but handle coercing of the files parameter types this.project.metaClass.invokeMethod = { String name,args -> if (name == 'exec') { // Call special instance to track verifications mySpecialInstance.exec((Closure) args.first()) } else { // This calls the delegate without causing infinite recursion // https://stackoverflow.com/a/10126006/1509221 MetaMethod metaMethod = delegate.class.metaClass.getMetaMethod(name,args) logInvokeMethod(name,args,metaMethod) // Special case 'files' method which can throw exceptions if (name == 'files') { // Coerce the arguments to match the signature of Project.files(Object... paths) // TODO: is there a way to do this automatically,e.g. coerceArgumentsToClasses? assert 0 == args.size() || 1 == args.size() if (args.size() == 0 || // files() args.first() == null) { // files(null) return metaMethod?.invoke(delegate,[[] as Object[]] as Object[]) } else { // files(ArrayList) possibly,so cast ArrayList to Object[] return metaMethod?.invoke(delegate,[(Object[]) args.first()] as Object[]) } } else { // Normal pass through return metaMethod?.invoke(delegate,args) } } } (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
- delphi – 使用Set8087CW,SetMXCSR和TWebBrowser屏蔽浮点异
- groovy环境搭建
- 附录A 使用OPENTOOLS API的Delphi扩展示例
- lua学习笔记
- golang 中Pointers Vs References
- Nice Patterns Strike Back(位运算dp+大数矩阵快速幂)
- Lua查找表元素过程(元表、__index方法是如何工作的)
- OSG+CEGUI+lua 程序框架OpenSceneGraph-2.8.2+CEGUI-0.6.2-
- perl – WWW ::机械化和广角警告
- grpc(4):使用 golang 调用consul api 接口,注册user-to