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

解决Swing测试中EDT内异常被吞噬导致错误测试未失败的问题

解决Swing测试中EDT内异常被吞噬导致错误测试未失败的问题

我来帮你分析下问题所在,以及怎么修复这个让错误测试“蒙混过关”的问题~

问题原因

你的EdtExtension里有两个关键逻辑导致异常被悄悄吞噬,让JUnit完全没察觉到测试出错:

  1. Try.call吞掉了异常:invokeInEdt方法用Try.call(() -> invokeAndWait(invocation))执行EDT调用,但JUnit Platform的Try.call会把所有异常封装成Try.Failure,却不会把异常重新抛给测试框架,导致框架误以为测试正常完成。
  2. 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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.07 08:43:09