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

Scala CLI执行测试后进程无法退出问题求助

Scala CLI执行测试后进程挂起问题排查

问题描述

  • 执行命令 scala-cli test core 时,测试已执行完成,但进程始终挂起无法退出,调试模式也无法定位原因
  • 使用munit框架时问题偶发:同步测试可正常退出,部分异步测试挂起
  • 切换至scalatest框架后问题必现:无论同步/异步测试,进程均无法退出
  • 已简化测试为仅执行 assert(true),问题依旧存在

补充测试示例(Scalatest异步测试)

import org.scalatest.funspec.{AsyncFunSpec, AsyncFunSpecLike}
import org.scalatest.funsuite.AsyncFunSuite

class SlackAPITest extends AsyncFunSuite {
  implicit val ec: scala.concurrent.ExecutionContext = scala.concurrent.ExecutionContext.global

  test("post message") {
    postMessage("whatever :chococat: :catbus: :catbus2:", testChannelId)
  }
}

注:postMessage 方法返回 Future[String]

可能的解决方向

1. 避免使用全局ExecutionContext

全局ExecutionContext(scala.concurrent.ExecutionContext.global)依赖JVM的ForkJoinPool,测试完成后可能有后台线程未终止导致进程挂起。建议创建专用ExecutionContext,并在测试结束后手动关闭:

import org.scalatest.funsuite.AsyncFunSuite
import org.scalatest.BeforeAndAfterAll
import scala.concurrent.ExecutionContext
import java.util.concurrent.Executors

class SlackAPITest extends AsyncFunSuite with BeforeAndAfterAll {
  private val executor = Executors.newFixedThreadPool(4)
  implicit override val executionContext: ExecutionContext = ExecutionContext.fromExecutor(executor)

  override def afterAll(): Unit = {
    executor.shutdown()
    // 若使用外部客户端(如Slack API客户端),在此添加关闭逻辑
    // client.close()
  }

  test("post message") {
    postMessage("whatever :chococat: :catbus: :catbus2:", testChannelId)
  }
}

2. 检查依赖版本兼容性

确认Scala CLI与Scalatest的版本匹配:

  • 升级Scala CLI至最新稳定版:执行 scala-cli self-update
  • 确保Scalatest版本适配当前Scala版本(如Scala 2.13/3.x对应Scalatest 3.2.+)

3. 排查未清理的资源

检查测试中是否存在未关闭的外部资源:

  • HTTP客户端连接池(如Slack API客户端是否未关闭)
  • 数据库连接、文件流等
    这些资源的后台线程可能阻止进程退出,需在测试结束后手动释放。

4. 临时强制退出方案(不推荐长期使用)

若暂时无法定位根本原因,可在测试类中添加AfterAll钩子强制退出进程:

import org.scalatest.funsuite.AsyncFunSuite
import org.scalatest.BeforeAndAfterAll

class SlackAPITest extends AsyncFunSuite with BeforeAndAfterAll {
  implicit val ec: scala.concurrent.ExecutionContext = scala.concurrent.ExecutionContext.global

  override def afterAll(): Unit = {
    System.exit(0)
  }

  test("post message") {
    postMessage("whatever :chococat: :catbus: :catbus2:", testChannelId)
  }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 10:42:15