Spring Boot Cucumber测试因被调用模块System.exit终止如何解决
你配置的System Rules不生效是因为Cucumber测试的执行生命周期由Cucumber独立的运行器管理,并不识别JUnit的@Rule注解,因此定义在步骤类中的拦截规则根本不会被加载触发。
可行解决方案
方案1:使用System Lambda库(适配Cucumber,推荐)
System Lambda是System Rules的无Rule衍生版本,不需要依赖JUnit的生命周期注解,可直接嵌入任意测试代码中拦截System.exit调用。
首先引入Maven依赖:
<dependency> <groupId>com.github.stefanbirkner</groupId> <artifactId>system-lambda</artifactId> <version>1.2.1</version> <scope>test</scope> </dependency>
修改步骤定义代码如下:
import static com.github.stefanbirkner.systemlambda.SystemLambda.catchSystemExit; import static org.junit.jupiter.api.Assertions.assertEquals; public class StepDefs{ @When("^the function is executed$") public void functionIsExecuted() throws Exception { System.out.println("This is executed right before System.exit()."); String[] args = {"some args"}; // 拦截System.exit调用,直接获取退出状态码 int exitCode = catchSystemExit(() -> { new MailExecutor().main(args); }); // 自行校验退出状态码是否符合预期 assertEquals(0, exitCode); System.out.println("This is executed AFTER System.exit() method!"); } }
方案2:自定义安全管理器(无额外依赖,兼容低版本Java)
如果不想引入第三方依赖,可通过自定义SecurityManager拦截退出权限,注意该方案在Java 17及以上版本中已被标记为废弃,高版本Java不推荐使用:
// 在步骤类的静态初始化块中添加如下代码 static { System.setSecurityManager(new SecurityManager() { @Override public void checkExit(int status) { // 抛出自定义异常阻止进程退出,后续自行捕获处理即可 throw new UnsupportedOperationException("拦截到System.exit调用,退出码:" + status); } @Override public void checkPermission(java.security.Permission perm) { // 放开其他所有权限避免影响正常测试逻辑 } }); }
方案3:解耦业务逻辑与退出逻辑(最优解,可长期维护)
如果有权限修改被测试的XX项目代码,建议调整微入口的实现结构,将业务执行逻辑和System.exit调用拆分:
// 改造后的XX项目入口类示例 public class MailExecutor { // 抽离业务逻辑,直接返回执行状态码 public static int execute(String[] args) { // 原有业务逻辑实现 return 0; } public static void main(String[] args) { int status = execute(args); System.exit(status); } }
测试时直接调用execute方法即可,完全不需要处理System.exit的拦截问题。
内容的提问来源于stack exchange,提问作者Alex Man
相关产品推荐
相关产品推荐

