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

Scala项目中TestContainers调用Docker Compose启动失败的解决咨询

解决Testcontainers Docker Compose找不到的问题及测试代码修正

一、解决Docker Compose找不到的错误

报错核心是Testcontainers无法在PATH中找到docker-compose命令,分两种场景解决:

1. 使用新版Docker Desktop(已整合docker-compose)

新版Docker Desktop将docker-compose整合到Docker CLI中,命令变为docker compose(无横杠)。需设置环境变量让Testcontainers识别这个命令:

  • 运行测试前执行:
    export TESTCONTAINERS_DOCKER_COMPOSE_COMMAND="docker compose"
    
  • 或在项目build.sbt中添加全局配置:
    javaOptions += "-DTESTCONTAINERS_DOCKER_COMPOSE_COMMAND=docker compose"
    

2. 使用旧版Docker(需独立安装docker-compose)

如果是旧版Docker Engine,需单独安装docker-compose工具,并确保它被添加到系统PATH环境变量:

  • 安装后验证可用性:
    docker-compose --version
    
    能输出版本信息说明PATH配置正确。

二、修正测试代码的逻辑问题

你的测试当前连接的是公共MQTT服务器broker.hivemq.com,并未使用Testcontainers启动的本地容器,需修改连接地址并调整异步逻辑:

class MqttClientFactoryTest extends AsyncFunSuite
    with Matchers with TestContainersForAll {

  implicit val ec: scala.concurrent.ExecutionContext = scala.concurrent.ExecutionContext.global

  override type Containers = DockerComposeContainer

  override def startContainers(): DockerComposeContainer = {
    DockerComposeContainer
      .Def(
        composeFiles = new File(
          this.getClass.getClassLoader
            .getResource("docker-compose-mqtt.yaml")
            .getFile
        ),
        exposedServices = Seq(ExposedService(name = "mqtt5", port = 1883))
      )
      .start()
  }

  test("mosquitto container") {
    withContainers { container =>
      // 获取本地容器的MQTT访问地址
      val mqttHost = container.getServiceHost("mqtt5", 1883)
      val mqttPort = container.getServicePort("mqtt5", 1883)

      val client: Mqtt5AsyncClient = Mqtt5Client
        .builder()
        .identifier(UUID.randomUUID().toString)
        .serverHost(mqttHost)
        .serverPort(mqttPort)
        .buildAsync()

      // 先订阅再发布,保证消息能被接收
      val subscribeFuture = client
        .subscribeWith()
        .topicFilter("test/topic")
        .qos(MqttQos.EXACTLY_ONCE)
        .callback(x => println(new String(x.getPayloadAsBytes)))
        .send()
        .asScala

      val publishFuture = subscribeFuture.flatMap { _ =>
        client
          .connect()
          .thenCompose(_ =>
            client
              .publishWith()
              .topic("test/topic")
              .payload("some random message!!!".getBytes)
              .send()
          )
          .asScala
      }

      // 组合异步操作,确保测试等待所有流程完成
      publishFuture.map { _ =>
        1 should be(1)
      }
    }
  }
}

代码修正说明:

  • 通过container.getServiceHost和container.getServicePort获取本地容器的实际访问地址
  • 调整执行顺序为先订阅再发布,避免消息丢失
  • 组合Future对象,确保测试不会提前结束

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 04:32:16