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

Spring Boot集成测试中如何在Awaitility中使用认证上下文?

问题:Spring Boot集成测试中Awaitility无法访问认证上下文导致测试失败

在Spring Boot集成测试中使用Awaitility时,测试前已设置认证上下文,但由于Awaitility的断言运行在独立线程,无法访问该上下文,导致测试失败。简化后的代码如下:

@Test
public void testMyServiceWithAuthentication() {
    // 登录用户

    myService.performAction();

    // 使用Awaitility等待条件满足
    await().atMost(10, SECONDS).until(() -> {
        // 该断言运行在独立线程,无法访问主线程的认证上下文
        return myService.checkCondition();
    });
}

当Awaitility断言执行时,无法获取主线程设置的认证上下文,导致myService.checkCondition()因缺失认证而调用失败。需要解决如何让Awaitility断言访问认证上下文,或找到依赖认证的同线程等待替代方案。


解决方案

方案一:手动传递认证上下文到Awaitility线程

Spring Security的SecurityContextHolder默认通过ThreadLocal存储上下文,Awaitility的线程无法共享主线程的ThreadLocal数据。可以在主线程提前获取认证对象,在断言的lambda中手动设置到当前线程的上下文,执行完成后清理避免污染:

@Test
public void testMyServiceWithAuthentication() {
    // 登录用户,获取当前认证上下文
    Authentication auth = SecurityContextHolder.getContext().getAuthentication();

    myService.performAction();

    await().atMost(10, SECONDS).until(() -> {
        // 在Awaitility线程中设置认证上下文
        SecurityContextHolder.getContext().setAuthentication(auth);
        try {
            return myService.checkCondition();
        } finally {
            // 执行完成后清理上下文,避免影响其他线程
            SecurityContextHolder.clearContext();
        }
    });
}

方案二:自定义Awaitility线程池统一注入上下文

如果多个测试用例都需要传递认证上下文,可以自定义Awaitility的线程池,在每个线程初始化时注入主线程的认证信息:

@BeforeEach
void setupAwaitility() {
    Authentication auth = SecurityContextHolder.getContext().getAuthentication();
    ExecutorService executor = Executors.newCachedThreadPool(runnable -> {
        Thread thread = new Thread(runnable);
        // 继承主线程的类加载器
        thread.setContextClassLoader(Thread.currentThread().getContextClassLoader());
        // 为新线程设置认证上下文
        SecurityContextHolder.getContext().setAuthentication(auth);
        return thread;
    });
    Awaitility.setExecutorService(executor);
}

@AfterEach
void teardownAwaitility() {
    // 重置Awaitility配置,避免影响其他测试
    Awaitility.reset();
}

方案三:同线程轮询替代Awaitility

如果不想使用多线程等待,可以自行实现同线程的轮询逻辑,这样自然能访问主线程的认证上下文:

@Test
public void testMyServiceWithAuthentication() {
    // 登录用户
    myService.performAction();

    long timeoutMillis = 10000;
    long pollIntervalMillis = 500;
    long startTime = System.currentTimeMillis();
    boolean conditionMet = false;

    while (System.currentTimeMillis() - startTime < timeoutMillis) {
        if (myService.checkCondition()) {
            conditionMet = true;
            break;
        }
        try {
            Thread.sleep(pollIntervalMillis);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw new RuntimeException("等待被中断", e);
        }
    }

    assertTrue("超时未满足预期条件", conditionMet);
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 05:57:20