【发布时间】:2018-07-21 06:09:46
【问题描述】:
这里的模拟类是org.apache.lucene.document.TextField。 setStringValue 是 void。
我的规范看起来像这样...
given:
...
TextField textFieldMock = GroovyMock( TextField )
// textField is a field of the ConsoleHandler class, ch is a Spy of that class
ch.textField = textFieldMock
// same results with or without this line:
textFieldMock.setStringValue( _ ) >> null
// NB I explain about this line below:
textFieldMock.getClass() >> Object.class
对应的应用代码如下所示:
assert textField != null
singleLDoc.add(textField)
writerDocument.paragraphIterator.each{
println( "textField == null? ${ textField == null }" )
println( "textField ${ textField.getClass() }" )
textField.setStringValue( it.textContent ) // NB this is line 114
indexWriter.addDocument( singleLDoc )
printlns 的输出是
textField == null? false
textField class java.lang.Object
... 这往往证明模拟正在发生并且getClass 被成功替换。如果我去掉 textFieldMock.getClass() >> Object.class 行,我会得到这个输出:
textField == null? false
textField null
在这两种情况下,失败都发生在下一行:
java.lang.NullPointerException
at org.apache.lucene.document.Field.setStringValue(Field.java:307)
at org.spockframework.mock.runtime.GroovyMockMetaClass.doInvokeMethod(GroovyMockMetaClass.java:86)
at org.spockframework.mock.runtime.GroovyMockMetaClass.invokeMethod(GroovyMockMetaClass.java:42)
at core.ConsoleHandler.parse_closure1(ConsoleHandler.groovy:114)
第 114 行是 setStringValue 行。 Field 这里是TextField 的(非final)超类。
对我来说,似乎发生了一些有趣的事情:好像 Spock 在自言自语:“啊,这个类 TextField 是 final,所以我将咨询它的父类,并使用方法 setStringValue从那里......我发现/决定它不是一个模拟......”
为什么setStringValue 没有被嘲笑(或“替代”或任何对方法的正确术语......)?
稍后
我去查看了相关包中的 Field.java。相关行是:
public void setStringValue(String value) {
if (!(fieldsData instanceof String)) {
throw new IllegalArgumentException("cannot change value type from " + fieldsData.getClass().getSimpleName() + " to String");
}
if (value == null) {
throw new IllegalArgumentException("value must not be null");
}
fieldsData = value;
}
... 第 307 行(涉及 NPE)原来是第一行 throw new IllegalArgumentException...。很奇怪。建议 fieldsData 是 null(如您所料)。
但是,为什么 Spock 发现自己在处理 Field 类中的这段代码呢?不合逻辑:这是在嘲弄,Jim,但不是我们所知道的那样。
PS我后来用(真实的)ConsoleHandler 进行了尝试,得到了相同的结果。我刚刚注意到,当 Spock 输出建议您使用 GroovyMock 时,它会说“如果被测代码是用 Groovy 编写的,请使用 Groovy 模拟”。这个类不是......但到目前为止,在我的测试代码中,我已经将 GroovyMock 用于 Java 包中的几个 Java 类,包括来自 Lucene 的其他类......没有这个问题......
PPS 解决方法我一无所获,最后只是创建了一个包装器类,它封装了有问题的 final TextField (并将发芽任何需要的方法......)。
我过去曾与 Lucene 类作斗争:其中许多结果是 final 或具有 final 方法。在有人指出您不需要测试已经可以信任的包之前(我同意这一点!),您仍然需要在开发代码时测试自己对此类类的使用。
【问题讨论】:
-
你能想出一个我可以运行的小例子来说明你的问题吗?
-
@tim_yates kriegaex 设法重现了这个问题(虽然我有点困惑,因为
TextField有一个继承方法setStringValue(不是stringValue),但没有getStringValue,所以我什至不确定这是否真的是一个财产)。如果你有时间,也许你想看看我的“答案”,我展示了(某种)成功使用这种“全局 GroovyMock”技术。
标签: groovy mocking spock final