In my JavaFX application I am using Spock and Groovy for testing.
I have dedicated WebBrowserController
for taking care of my JavafX WebView
component. I wanted to test some functionalities that depend on current Location and Document of the WebView
.
Relevant part of WebBrowserController:
public WebEngine getEngine() {
return panel.getWebView().getEngine();
}
This is how I create an instance of WebBrowserController
for my tests.
Notice the GroovyMock
I used there - ordinary Mock(...)
does not work for final classes and WebEngine
is a final class.
WebBrowserController getMockedControllerWithDocument(Document document) {
WebBrowserController controller = Mock(WebBrowserController)
controller.getEngine() >> GroovyMock(WebEngine) {
getDocument() >> document
getLocation() >> "some random string"
}
controller
}
The line below is under the test and it breaks. I would expect "some random string" to be returned but I just get failed test and NPE.
String url = controller.get().getEngine().getLocation()
Now the interesting part - I have examined the instance of WebEngine
in two places - at the end of getMockedControllerWithDocument
and at the line pasted above. What I found out is that it referenced the same object. Yet, when I invoked any of the stubbed methods outside of test code I was hit by NPE - getLocation()
executed the actual implementation and not the stub (the original method is not just a simple getter and it uses a wrapped value in between).
Summing it up: why the hell does the exact same object behaves differently depending on the place its methods are being invoked?
Because GroovyMock
, GroovySpy
and GroovyStub
only work as you expect for Groovy classes. When called by Java classes, they behave like normal Spock mocks. This is documented 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.