为何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
相关产品推荐
相关产品推荐

