Spock 中 final class 的模拟实例在测试和开发代码中的行为不同

Mocked instance of final class in Spock behaves differently in test and dev code

在我的 JavaFX 应用程序中,我使用 Spock 和 Groovy 进行测试。 我专门 WebBrowserController 来处理我的 JavafX WebView 组件。我想测试一些依赖于 WebView.

当前位置和文档的功能

WebBrowserController 的相关部分:

public WebEngine getEngine() {
    return panel.getWebView().getEngine();
}

这就是我为测试创建 WebBrowserController 实例的方式。 请注意我在那里使用的 GroovyMock - 普通 Mock(...) 不适用于最终的 classes 并且 WebEngine 是最终的 class.

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

    controller
}

下面的行正在测试中,它中断了。我希望返回 "some random string" 但我只是测试失败和 NPE。

String url = controller.get().getEngine().getLocation()

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

总结一下:为什么完全相同的对象会根据调用其方法的位置而表现不同?

因为 GroovyMockGroovySpyGroovyStub 仅按您对 Groovy 类 的预期工作。当被 Java 类 调用时,它们的行为就像正常的 Spock 模拟。这记录在案 here:

TIP

When Should Groovy Mocks be Favored over Regular Mocks? Groovy mocks should be used when the code under specification is written in Groovy and some of the unique Groovy mock features are needed. When called from Java code, Groovy mocks will behave like regular mocks. Note that it isn’t necessary to use a Groovy mock merely because the code under specification and/or mocked type is written in Groovy. Unless you have a concrete reason to use a Groovy mock, prefer a regular mock.