Spock 测试绿色,尽管应用程序代码抛出异常
Spock test green although app code threw Exception
我正在使用 Groovy 和 Gradle 进行测试。
我的应用程序代码中有以下几行:
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 )
}
如果我模拟(使用 GroovyMock
因为 javafx.stage.FileChooser
是 final
)所以 fc.showOpenDialog
returns null
我会期待 NullPointerException
被扔到 file.exists()
... 一个是。
但这并没有出现在测试结果中,所有结果都是绿色的。您发现发生这种情况的唯一方法是查看 class 的测试结果:然后您会看到显示 "StdErr" 的灰色按钮。
这似乎是因为包围 Runnable
是 "swallowing" 它...
Spock 中是否有任何方法可以在 Runnable
中产生异常导致测试失败?
PS 测试代码:
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( _ )
}
这是应用代码的 SSCCE
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()
}
}
唯一可能需要解释的是 Spock 测试代码中的 systemInMock
,它让测试向应用程序代码中的 StdIn
提供文本:
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()
我的 Gradle dependencies
子句中对应的行是:
testCompile 'com.github.stefanbirkner:system-rules:1.16.0'
...但我认为这可以通过在 Groovy 测试文件中使用 @Grab
来获得,对吗?
我想我现在意识到这个问题实际上是关于如何最好地在 Spock 中进行 JavaFX 应用程序线程测试...使用 Swing 进行 EDT 测试一直是个问题,毫无疑问,类似的事情也适用。这一切都回到了我身边:当你得到 Platform
到 运行 和 Runnable
时,随着调用者代码继续前进,从 runLater
抛出异常是没有意义的,所以我相信唯一现实的选择是 run()
调用测试代码可以调用的其他方法...
我搜索了 "Spock JavaFX testing",但搜索结果不多...
这是我认为唯一可行的前进方式:像这样改变这一点:
else if ( response == 'o' ){
Platform.runLater(new Runnable() {
void run() {
openFileChooser()
}
})
}
ConsoleHandler 的新方法:
def openFileChooser() {
FileChooser fc = getFileChooser()
File file = fc.showOpenDialog( null )
// if( file != null && file.exists() ) {
if( file.exists() ) {
analyseFile( file )
}
}
新的 Spock 测试:
def "getting a null result from the showOpenDialog should be handled OK2"(){
given:
FileChooser fc = GroovyMock( FileChooser )
ConsoleHandler ch = Spy( ConsoleHandler ){ getFileChooser() >> fc }
fc.showOpenDialog( _ ) >> null
when:
com.sun.javafx.application.PlatformImpl.startup( {} )
ch.openFileChooser()
Thread.sleep( 1000L )
then:
0 * ch.analyseFile( _ )
}
测试失败(万岁!):
java.lang.NullPointerException: Cannot invoke method exists() on null
object
但我欢迎更有经验的 Groovy/Spock 提供关于这是否可以改进的意见。
确实我有点疑惑为什么我在when:
子句中调用openFileChooser
时没有得到"incorrect State"。我认为这是因为在 mock FileChooser
上进行 showOpenDialog
实际上并不涉及任何检查以确保我们当前处于 Java FX 应用程序线程。原来 ...PlatformImpl.startup( {} )
和 Thread.sleep
行可以在该测试中删除。这是理想的做法吗?!嗯...
以后
将此作为唯一答案关闭...实际上,与在 JavaFX-app-thread 中使用普通旧 JUnit 在普通旧 Java 中测试代码一样,同样的考虑适用于这个问题. Spock + Groovy 可能会带来一些问题,或者它们可能会使它变得更容易......我还没有足够的经验来了解它们。
我正在使用 Groovy 和 Gradle 进行测试。
我的应用程序代码中有以下几行:
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 )
}
如果我模拟(使用 GroovyMock
因为 javafx.stage.FileChooser
是 final
)所以 fc.showOpenDialog
returns null
我会期待 NullPointerException
被扔到 file.exists()
... 一个是。
但这并没有出现在测试结果中,所有结果都是绿色的。您发现发生这种情况的唯一方法是查看 class 的测试结果:然后您会看到显示 "StdErr" 的灰色按钮。
这似乎是因为包围 Runnable
是 "swallowing" 它...
Spock 中是否有任何方法可以在 Runnable
中产生异常导致测试失败?
PS 测试代码:
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( _ )
}
这是应用代码的 SSCCE
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()
}
}
唯一可能需要解释的是 Spock 测试代码中的 systemInMock
,它让测试向应用程序代码中的 StdIn
提供文本:
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()
我的 Gradle dependencies
子句中对应的行是:
testCompile 'com.github.stefanbirkner:system-rules:1.16.0'
...但我认为这可以通过在 Groovy 测试文件中使用 @Grab
来获得,对吗?
我想我现在意识到这个问题实际上是关于如何最好地在 Spock 中进行 JavaFX 应用程序线程测试...使用 Swing 进行 EDT 测试一直是个问题,毫无疑问,类似的事情也适用。这一切都回到了我身边:当你得到 Platform
到 运行 和 Runnable
时,随着调用者代码继续前进,从 runLater
抛出异常是没有意义的,所以我相信唯一现实的选择是 run()
调用测试代码可以调用的其他方法...
我搜索了 "Spock JavaFX testing",但搜索结果不多...
这是我认为唯一可行的前进方式:像这样改变这一点:
else if ( response == 'o' ){
Platform.runLater(new Runnable() {
void run() {
openFileChooser()
}
})
}
ConsoleHandler 的新方法:
def openFileChooser() {
FileChooser fc = getFileChooser()
File file = fc.showOpenDialog( null )
// if( file != null && file.exists() ) {
if( file.exists() ) {
analyseFile( file )
}
}
新的 Spock 测试:
def "getting a null result from the showOpenDialog should be handled OK2"(){
given:
FileChooser fc = GroovyMock( FileChooser )
ConsoleHandler ch = Spy( ConsoleHandler ){ getFileChooser() >> fc }
fc.showOpenDialog( _ ) >> null
when:
com.sun.javafx.application.PlatformImpl.startup( {} )
ch.openFileChooser()
Thread.sleep( 1000L )
then:
0 * ch.analyseFile( _ )
}
测试失败(万岁!):
java.lang.NullPointerException: Cannot invoke method exists() on null object
但我欢迎更有经验的 Groovy/Spock 提供关于这是否可以改进的意见。
确实我有点疑惑为什么我在when:
子句中调用openFileChooser
时没有得到"incorrect State"。我认为这是因为在 mock FileChooser
上进行 showOpenDialog
实际上并不涉及任何检查以确保我们当前处于 Java FX 应用程序线程。原来 ...PlatformImpl.startup( {} )
和 Thread.sleep
行可以在该测试中删除。这是理想的做法吗?!嗯...
以后
将此作为唯一答案关闭...实际上,与在 JavaFX-app-thread 中使用普通旧 JUnit 在普通旧 Java 中测试代码一样,同样的考虑适用于这个问题. Spock + Groovy 可能会带来一些问题,或者它们可能会使它变得更容易......我还没有足够的经验来了解它们。