Java Spock中最终类的模拟实例在测试和开发代码中的行为不同

Java Spock中最终类的模拟实例在测试和开发代码中的行为不同,java,javafx,groovy,webview,spock,Java,Javafx,Groovy,Webview,Spock,在我的JavaFX应用程序中,我使用Spock和Groovy进行测试。 我有专门的WebBrowserController来管理我的JavafXWebView组件。我想测试一些依赖于WebView当前位置和文档的功能 WebBrowserController的相关部分: public WebEngine getEngine() { return panel.getWebView().getEngine(); } 这就是我为测试创建WebBrowserController实例的方法。 请

在我的JavaFX应用程序中,我使用Spock和Groovy进行测试。 我有专门的
WebBrowserController
来管理我的JavafX
WebView
组件。我想测试一些依赖于
WebView
当前位置和文档的功能

WebBrowserController的相关部分:

public WebEngine getEngine() {
    return panel.getWebView().getEngine();
}
这就是我为测试创建
WebBrowserController
实例的方法。 请注意,我在那里使用的
GroovyMock
普通
Mock(…)
不适用于最终类,而
WebEngine
是最终类

WebBrowserController getMockedControllerWithDocument(Document document) {
    WebBrowserController controller = Mock(WebBrowserController)
    controller.getEngine() >> GroovyMock(WebEngine) {
        getDocument() >> document
        getLocation() >> "some random string"
    }

    controller
}
下面这条线正在测试中,它断了。我希望返回“一些随机字符串”,但我只是得到了失败的测试和NPE

String url = controller.get().getEngine().getLocation()
现在,有趣的部分-我在两个地方检查了
WebEngine
的实例-在
getMockedControllerWithDocument
的末尾和上面粘贴的行。我发现它引用了同一个对象。然而,当我在测试代码之外调用任何存根方法时,我被NPE-
getLocation()
击中,执行的是实际的实现,而不是存根(原始方法不仅仅是一个简单的getter,它在两者之间使用了一个包装值)


总结一下:为什么完全相同的对象的行为会因其方法被调用的位置不同而有所不同?

因为
GroovyMock
GroovySpy
GroovyStub
只能像您期望的那样对Groovy类起作用。当被Java类调用时,它们的行为类似于普通的Spock模拟。这是记录在案的:

提示 什么时候Groovy模拟应该比常规模拟更受欢迎?当规范下的代码是用Groovy编写的,并且需要一些独特的Groovy模拟特性时,应该使用Groovy模拟从Java代码调用Groovy mock时,Groovy mock的行为将类似于常规mock。请注意,没有必要仅仅因为规范和/或mock类型下的代码是用Groovy编写的而使用Groovy mock。除非您有具体的理由使用Groovy模拟,否则最好使用常规模拟


它确实回答了我的问题,谢谢!关于如何模拟最终类实例有什么建议吗?如果配置正确,您可以将Mockito或PowerMock与Spock结合使用。但通常当有人想要使用PowerMock时,在我看来,这是一种不好的味道,更确切地说是一种对重构的呼吁,而不是使用肮脏的伎俩。答案取决于您的具体情况,但您也可以在控制器中提供包装方法
getDocument()
getLocation()
,然后轻松地将它们存根。为什么要测试您的mock实际返回另一个mock?但我能感觉到你的痛苦,因为你正在处理一个外部的期末课程。所以你有选择,自己决定。