解决Swing测试中EDT内异常被吞噬导致错误测试未失败的问题
解决Swing测试中EDT内异常被吞噬导致错误测试未失败的问题
我来帮你分析下问题所在,以及怎么修复这个让错误测试“蒙混过关”的问题~
问题原因
你的EdtExtension里有两个关键逻辑导致异常被悄悄吞噬,让JUnit完全没察觉到测试出错:
Try.call吞掉了异常:invokeInEdt方法用Try.call(() -> invokeAndWait(invocation))执行EDT调用,但JUnit Platform的Try.call会把所有异常封装成Try.Failure,却不会把异常重新抛给测试框架,导致框架误以为测试正常完成。- EDT异常封装处理不当:在
invokeAndWait的Lambda中,你把异常包装成AssertionError抛出,但这个异常会被SwingUtilities.invokeAndWait自动捕获,封装到InvocationTargetException里,后续没有正确解包并传递给JUnit。
修复方案
我们需要调整异常传播逻辑,确保EDT内抛出的异常能完整传递给JUnit测试框架,让失败的测试如实报错。
修正后的EdtExtension代码
import org.fest.swing.annotation.RunsInEDT; import org.junit.jupiter.api.extension.ExtensionContext; import org.junit.jupiter.api.extension.InvocationInterceptor; import org.junit.jupiter.api.extension.ReflectiveInvocationContext; import javax.swing.SwingUtilities; import java.lang.annotation.Retention; import java.lang.reflect.AnnotatedElement; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; public class EdtExtension implements InvocationInterceptor { @Override public void interceptTestMethod(Invocation<Void> invocation, ReflectiveInvocationContext<Method> invocationContext, ExtensionContext extensionContext) throws Throwable { if (shouldRunInEdt(invocationContext)) { invokeInEdt(invocation); } else { invocation.proceed(); } } /** * @apiNote FEST's {@link RunsInEDT} has a default {@link Retention} and hence is not reflectively accessible. We need to check our own runtime annotation instead. */ private static boolean shouldRunInEdt(ReflectiveInvocationContext<Method> invocationContext) { Class<?> testClass = invocationContext.getTargetClass(); Method testMethod = invocationContext.getExecutable(); return hasEdtAnnotation(testClass) || hasEdtAnnotation(testMethod); } private static boolean hasEdtAnnotation(AnnotatedElement annotatedElement) { return annotatedElement.getAnnotation(EdtRun.class) != null; } // 移除Try.call,直接传播异常给JUnit框架 private void invokeInEdt(Invocation<Void> invocation) throws Throwable { invokeAndWait(invocation); } private Void invokeAndWait(Invocation<Void> invocation) throws Throwable { try { SwingUtilities.invokeAndWait(() -> { try { invocation.proceed(); } catch (Throwable e) { // 直接抛出原始异常,让invokeAndWait自动封装 throw e; } }); } catch (InvocationTargetException e) { // 解包InvocationTargetException,抛出原始错误给JUnit throw e.getTargetException(); } return null; } }
关键修改点说明
- 移除
Try.call:原来的Try.call会“吞下”异常,现在直接调用invokeAndWait并把所有异常向上传播,让JUnit框架能捕获到。 - 正确解包EDT异常:
SwingUtilities.invokeAndWait会把EDT内的异常封装到InvocationTargetException,我们手动解包这个异常,把原始的AssertionError或其他错误抛给JUnit,确保测试失败的原因清晰可见。 - 简化异常抛出逻辑:不再额外包装
AssertionError,直接抛出原始异常,避免错误信息被多层包装干扰。
验证修复
现在再跑你写的那个故意出错的测试:
@Test void ifNotAnnotated_runsInEdt() { boolean isEdt = EventQueue.isDispatchThread(); assertFalse(isEdt); // 这个AssertionError会正确传递给JUnit,测试会如预期失败 }
它就不会再“假装通过”,而是会正常抛出错误,提示你断言失败。
内容来源于stack exchange
相关产品推荐
相关产品推荐

