I'm using Groovy and Gradle for my testing.
I have the following lines in my app code:
Platform.runLater( new Runnable() {
void run() {
....
FileChooser fc = getFileChooser()
File file = fc.showOpenDialog( null )
// if( file != null && file.exists() ) { <--- this is what I have to put
if( file.exists() ) {
println( "file $file exists")
analyseFile( file )
}
If I mock (using GroovyMock because javafx.stage.FileChooser is final) so that fc.showOpenDialog returns null I would expect a NullPointerException to be thrown on file.exists()... and one is.
But this doesn't show up in the test results, which are all green. The only way you find that this has happened is when you go and look at the test results for the class: then you see that the grey button saying "StdErr" is present.
This appears to be because the enveloping Runnable is "swallowing" it...
Is there any way in Spock to make an Exception inside a Runnable lead to test failure?
PS test code:
def "getting a null result from the showOpenDialog should be handled OK"(){
given:
FileChooser fc = GroovyMock( FileChooser )
ConsoleHandler ch = Spy( ConsoleHandler ){ getFileChooser() >> fc }
ch.setMaxLoopCount 10
systemInMock.provideLines( "o" )
fc.showOpenDialog( _ ) >> null
when:
com.sun.javafx.application.PlatformImpl.startup( {} )
ch.loop()
Thread.sleep( 1000L ) // NB I know this is a rubbish way to handle things... but I'm a newb with Spock!
then:
0 * ch.analyseFile( _ )
}
here's the SSCCE for the app code
class ConsoleHandler {
int loopCount = 0
def maxLoopCount = Integer.MAX_VALUE - 1
def response
FileChooser fileChooser = new FileChooser()
void analyseFile( File file ) {
// ...
}
void loop() {
while( ! endConditionMet() ){
print '> '
response = System.in.newReader().readLine()
if( response ) response = response.toLowerCase()
if( response == 'h' || response == 'help' ){
println "Help: enter h for this help, q to quit"
}
else if ( response == 'o' ){
Platform.runLater( new Runnable() {
void run() {
FileChooser fc = getFileChooser()
File file = fc.showOpenDialog( null )
// if( file != null && file.exists() ) {
if( file.exists() ) {
analyseFile( file )
}
}
})
}
}
}
boolean endConditionMet() {
loopCount++
response == 'q' || loopCount > maxLoopCount
}
static void main( args ) {
com.sun.javafx.application.PlatformImpl.startup( {} )
new ConsoleHandler().loop()
com.sun.javafx.application.PlatformImpl.exit()
}
}
The only other thing that might require an explanation is systemInMock in the Spock test code, which lets the test supply text to StdIn in the app code:
import org.junit.contrib.java.lang.system.TextFromStandardInputStream
import static org.junit.contrib.java.lang.system.TextFromStandardInputStream.emptyStandardInputStream
....
// field of the Test class, of course:
@Rule
public TextFromStandardInputStream systemInMock = emptyStandardInputStream()
The corresponding line in my Gradle dependencies clause is :
testCompile 'com.github.stefanbirkner:system-rules:1.16.0'
... but I think this could be obtained by using a @Grab in the Groovy test file, right?
I think I now realise this question is really about how best to do JavaFX-app-thread testing in Spock... EDT testing with Swing was always a problem, no doubt similar things apply. And it's all coming back to me: when you get Platform to run the Runnable it would make no sense to throw an Exception from runLater as the caller code has moved on, so I believe the only realistic option is for run() to call some other method which the testing code can call...
I have searched for "Spock JavaFX testing" but not much has come up...