You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何JUnit的assertThrows()未捕获JavaFX属性抛出的异常?

问题描述

尝试通过JavaFX的ChangeListener在Property设置无效值时抛出IllegalArgumentException,但使用JUnit 5的assertThrows()测试该行为时,测试失败(提示“预期抛出IllegalArgumentException,但未捕获任何异常”),同时控制台却显示该异常已抛出。

环境信息:

  • 系统:Windows
  • IDE:NetBeans 23
  • Java版本:17.0.13
  • Gradle版本:8.12

相关代码文件:

settings.gradle

rootProject.name = 'PropertyLimiterQuestion2'

build.gradle

plugins {
    id 'org.openjfx.javafxplugin' version '0.1.0'
}
apply plugin: 'application'

repositories {
    mavenCentral()
}

test {
    useJUnitPlatform()
}

javafx {
    version = '21.0.4'
    modules = [ 'javafx.controls', 'javafx.fxml' ]
}
dependencies {
    testImplementation 'org.junit.jupiter:junit-jupiter-api:5.11.2'
    testImplementation 'org.junit.jupiter:junit-jupiter-params:5.11.2'
    testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
    testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.11.2'
}

测试类 PropertyValueLimiterTest.java

class PropertyValueLimiterTest {
    
    @Test
    void bind_invalidValue_MWE(){
        Set<String> allowedValues = Set.of("foo", "bar");
        Property<String> property = new SimpleObjectProperty<>("foo");
        ChangeListener<String> listener = (observable, oldValue, newValue) -> {
            if(! allowedValues.contains(newValue)){
                throw new IllegalArgumentException();
            }
        };
        property.addListener(listener);
        Assertions.assertThrows(IllegalArgumentException.class,
                () -> property.setValue("invalid"));
    }
}
问题原因

JavaFX的ObservableValueBase(SimpleObjectProperty的父类)在通知ChangeListener时,会捕获所有异常,并将异常交给当前线程的未捕获异常处理器处理,不会将异常传播回setValue()的调用栈。因此,setValue("invalid")方法本身不会抛出异常,JUnit的assertThrows()自然无法捕获到控制台打印的异常。

解决方案

不要用ChangeListener做值校验,正确的做法是自定义Property,在设置值的阶段提前校验,让异常直接从setValue()方法抛出。

示例代码如下:

class PropertyValueLimiterTest {
    
    @Test
    void bind_invalidValue_MWE(){
        Set<String> allowedValues = Set.of("foo", "bar");
        // 自定义校验逻辑的Property
        Property<String> property = new SimpleObjectProperty<>("foo") {
            @Override
            public void setValue(String newValue) {
                if (!allowedValues.contains(newValue)) {
                    throw new IllegalArgumentException("Invalid value: " + newValue);
                }
                super.setValue(newValue);
            }
        };
        
        Assertions.assertThrows(IllegalArgumentException.class,
                () -> property.setValue("invalid"));
    }
}

这样修改后,setValue()方法会在设置值前直接校验,异常会直接传播到调用处,JUnit的assertThrows()就能正常捕获并通过测试。

内容的提问来源于stack exchange,提问作者Anab

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 10:22:01