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

Cucumber监听器在Spring Boot上下文初始化前触发致空指针异常

解决Cucumber监听器在Spring上下文初始化前触发导致的NullPointerException

核心问题在于Cucumber的插件(ConcurrentEventListener)初始化和事件触发逻辑早于Spring Boot上下文加载流程。当TestRunStarted事件触发时,@SpringBootTest尚未完成上下文初始化,导致SpringContextHolder.getContext()返回null,抛出NullPointerException。以下是几种可行的解决方案:

方案1:改用Spring+TestNG生命周期钩子替代Cucumber监听器

直接在测试基类中利用TestNG的生命周期注解,确保Spring上下文初始化完成后再启动/停止Appium服务器,无需依赖Cucumber监听器:

修改BaseRunnerTest:

package org.example.core;

import io.cucumber.testng.AbstractTestNGCucumberTests;
import io.cucumber.testng.CucumberOptions;
import org.example.core.services.AppiumServerServices;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;

@SpringBootTest
@CucumberOptions(
    plugin = {"pretty"}, // 移除自定义监听器
    glue = "org.example.stepdefinitions" 
)
public abstract class BaseRunnerTest extends AbstractTestNGCucumberTests {

    @Autowired
    private AppiumServerServices appiumServerServices;

    @BeforeClass(alwaysRun = true)
    public void setupAppiumServer() {
        appiumServerServices.initAppiumServer();
    }

    @AfterClass(alwaysRun = true)
    public void teardownAppiumServer() {
        appiumServerServices.tearAppiumServer();
    }
}

TestNG的@BeforeClass会在所有测试用例执行前触发,且此时Spring上下文已完全初始化,@Autowired可正常注入Bean。此方案最简洁,无生命周期冲突问题,优先推荐。

方案2:改进SpringContextHolder,等待上下文初始化完成

若必须保留Cucumber监听器,可修改SpringContextHolder,监听Spring上下文刷新事件,确保获取上下文时已完成初始化:

package org.example.core.util;

import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
import java.util.concurrent.CountDownLatch;

@Component
public class SpringContextHolder implements ApplicationContextAware {

    private static ApplicationContext context;
    private static final CountDownLatch contextLatch = new CountDownLatch(1);

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) {
        context = applicationContext;
    }

    @EventListener(ContextRefreshedEvent.class)
    public void onContextRefreshed() {
        contextLatch.countDown();
    }

    public static ApplicationContext getContext() {
        try {
            contextLatch.await(); // 阻塞直到上下文初始化完成
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw new RuntimeException("等待Spring上下文初始化失败", e);
        }
        return context;
    }
}

修改后,AppiumServerListener中的SpringContextHolder.getContext()会等待上下文就绪后再返回,避免NPE。

方案3:使用Cucumber官方Spring集成规范

通过@CucumberContextConfiguration配置Cucumber的Spring上下文,确保Cucumber事件在Spring上下文初始化后触发:

  1. 创建Cucumber Spring配置类:
package org.example.core.config;

import org.springframework.boot.test.context.SpringBootTest;
import io.cucumber.spring.CucumberContextConfiguration;

@CucumberContextConfiguration
@SpringBootTest
public class CucumberSpringConfig {
}
  1. 修改BaseRunnerTest的glue路径,包含配置类所在包:
@CucumberOptions(
    plugin = {"pretty", "org.example.core.listeners.AppiumServerListener"},
    glue = {"org.example.stepdefinitions", "org.example.core.config"} 
)

此方式符合Cucumber官方Spring集成逻辑,会优先初始化Spring上下文再触发Cucumber事件。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 21:09:52