作为测试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
答案 0 :(得分:1)
在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)
}
}
这很有效,除了您可能会看到有关“错误的参数数量”或“无法在空对象上调用方法xxxxx”的异常。问题是上面的代码不处理方法参数的强制转换。对于project.files(Object... paths)
,invokeMethod的参数应为[['path1', 'path2']]
形式。但是,在某些情况下会调用files(null)
或files()
,因此invokeMethod的args分别为[null]
和[]
,因为它预期{{1} }}。产生上述错误。
以下代码仅针对[[]]
方法解决了这个问题,但这对我的单元测试来说已经足够了。我仍然希望找到一种更好的强制类型或更换单一方法的方法。
files