如果必须在JavaFX线程中调用方法,则看来我必须在when
块中执行以下操作:
... setup ...
// def throwable
when:
Platform.runLater( new Runnable(){
@Override
void run() {
// try {
log.debug "in runnable, calling run method ..."
someObject.methodWhichMustRunInJavaFXThread()
log.debug "... run method finished normally"
// }catch( Throwable t ){
// log.error( t.message, t )
// throwable = t
// }
}
})
WaitForAsyncUtils.waitForFxEvents()
log.debug "waitForFXEvents ended..."
// if throwable != null
// throw throwable
/* NB it appears that re-throwing the Throwable like this after waitForFxEvents
is probably the only way to bring it to the developer's attention!
PS I added this re-throwing idea only a few hours after submitting the question. I am
currently monitoring things to find whether this in fact solves the problem */
then:
// throwable == null
/* in fact this seems to be a rather naive check: from my experimentation,
regardless of whether caught in the above catch clause, it appears that if a throwable
is thrown in `run`, although "invocation counting tests" are performed in the "then"
block, this sort of "static" equality check will never be performed in the "then" block
*/
... other verifications...
但是我断断续续地发现,如果在run
方法中引发异常,则这种技术可能导致失败的可怕泄漏到后续测试。我认为,尝试取消任何注释行,如上面的代码所示,可以解决任何问题,但实际上不行:实际上是:在run
中抛出并由catch
子句捕获的throwable ,仍然可以归因于以后的方法!
这是一种误导性的Spock失败输出的示例:尽管此NPE在先前的测试方法中被抛出在Platform.runLater( ... )
中(实际上是在文件initial_load_testing.groovy中完全不同的Specification
中),但是此失败实际上归因于一个测试,该测试恰好在它之后的任意时间出现。
注意,提到“延迟异常” ...
java.lang.RuntimeException: java.lang.NullPointerException: Cannot invoke method setRoot() on null object
at org.testfx.util.WaitForAsyncUtils.---- Delayed Exception: (See Trace Below) ----(WaitForAsyncUtils.java:0)
Caused by: java.lang.NullPointerException: Cannot invoke method setRoot() on null object
at core.FileHandlingFramework.tryToLoadFile(filehandlingframework.groovy:83)
at core.StdFileHandlingFrameworkTemplate.tryToOpenFile(stdfilehandlingframeworktemplate.groovy:97)
at core.App.start(main.groovy:120)
at core.AppStdSpec2$1.run(initial_load_testing.groovy:92)
at com.sun.javafx.application.PlatformImpl.lambda$runLater$10(PlatformImpl.java:428)
at com.sun.javafx.application.PlatformImpl.lambda$runLater$11(PlatformImpl.java:427)
at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:96)
at com.sun.glass.ui.gtk.GtkApplication.lambda$runLoop$11(GtkApplication.java:277)
at java.base/java.lang.Thread.run(Thread.java:834)
[我发现奇怪的是,我确实允许JavaFX线程事件与WaitForAsyncUtils.waitForFxEvents()
发生“冲突” ...但是,似乎在某种程度上Spock框架有时可以检测到可抛出对象的抛出。 waitForFXEvents()
方法结束。
有解决方案吗?现象的间歇性是一个实际问题。
临时,以下解决方案出现可以通过在when
块结束之前重新抛出FX线程中捕获的任何异常来起作用:
... setup ...
def throwable
when:
Platform.runLater( new Runnable(){
@Override
void run() {
try {
someObject.methodWhichMustRunInJavaFXThread()
}catch( Throwable t ){
log.error( t.message, t )
throwable = t
}
}
})
WaitForAsyncUtils.waitForFxEvents()
if throwable != null
throw throwable
如chrylis -onstroke-所述,也可以使用闭包,因此实用程序方法可能如下所示:
void executeInFXThreadAndWait( def closure, def ... params ){
def fXThrowable
Platform.runLater({ it ->
try {
closure(params)
}catch( Throwable t ){
if( t.message != 'exit' ) {
log.error(t.message, t)
fXThrowable = t
}
}
})
WaitForAsyncUtils.waitForFxEvents()
if( fXThrowable != null )
throw fXThrowable
}
用法(App
扩展为Application
):
def myClosure = { args ->
App.instance.start( args[ 0 ] )
}
...
TestUtilities.instance.executeInFXThreadAndWait( myClosure, mockStage )
NB关于测试“退出”的事情是,有时我故意在测试中抛出异常,以从需要验证的方法已经被验证的方法“弹出”。如果Throwable
的消息为“退出”,它将被忽略。
假说:我猜测,Spock框架不知不觉中能够检测到异常是否已经“处理完毕”,并且如果这样重新抛出该异常,那么该框架将不会继续处理“延迟异常” 。欢迎Spock专家发表评论。