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

SBT多项目测试无法识别非托管JAR的问题求助

问题:SBT多项目测试时无法识别非托管JAR

我有一个SBT多项目结构,运行runMain时能正常识别/lib里的非托管JAR,但运行测试时却不行。我的目标是创建一个无实际实现的stub子项目,将其作为Provided依赖添加到主项目中,编译时用stub,运行和测试时用/lib里的实际JAR(该JAR仅实现了打印随机字符串的功能)。

项目结构

./MainClass
./MainClass/lib/printsomethingexample_2.13-0.1.0-SNAPSHOT.jar
./MainClass/src/test/scala/Main.scala
./MainClass/src/main/scala/Main.scala
./stub/lib/printsomethingexample_2.13-0.1.0-SNAPSHOT.jar
./stub/src/main/scala/test/stub/Example.scala
./build.sbt

关键文件内容

MainClass/src/main/scala/Main.scala

import test.stub.Example

object Main extends App {
  Example.printSomething()
}

MainClass/src/test/scala/Main.scala

import org.scalatest.flatspec._
import test.stub.Example

class Main extends AnyFlatSpec {

  "Unmanaged jar" should "work" in {
    Example.printSomething()
  }

}

stub/src/main/scala/test/stub/Example.scala

package test.stub

object Example {
  def printSomething(): Unit = ???
}

build.sbt

ThisBuild / version := "0.1.0-SNAPSHOT"

ThisBuild / scalaVersion := "2.13.10"

lazy val root = (project in file("."))
  .settings(
    name := "stubExample"
  ).aggregate(MainClass, stub)

lazy val stub = project
  .in(file("stub"))
  .settings(
    name := "stub"
  )

lazy val MainClass = project
  .in(file("MainClass"))
  .settings(
    name := "MainClass",
    libraryDependencies += "org.scalatest" %% "scalatest" % "3.2.14" % Test,
  ).dependsOn(`stub` % Provided)

运行情况

  • 执行sbt "MainClass/runMain Main"正常,输出如下:
▶ sbt "MainClass/runMain Main"
[info] welcome to sbt 1.6.2 (Azul Systems, Inc. Java 1.8.0_312)
[info] loading global plugins from /Users/riccardo/.sbt/1.0/plugins
[info] loading project definition from /Users/riccardo/Documents/Projects/stubExample/project
[info] loading settings for project root from build.sbt ...
[info] set current project to stubExample (in build file:/Users/riccardo/Documents/Projects/stubExample/)
[info] running Main
Printed Something
  • 执行sbt "MainClass/test"报错:
[info] Main:
[info] Unmanaged jar
[info] - should work *** FAILED ***
[info]   scala.NotImplementedError: an implementation is missing

补充说明

非托管JAR里的test.stub.Example实现如下:

package test.stub

object Example {
  def printSomething(): Unit = println("Printed Something")
}

我原本以为Provided依赖会把stub从运行类路径中排除,从而让JVM优先加载/lib里的实际实现,但测试时却还是用了stub的未实现版本。如果移除dependsOn,测试能正常运行,但没有非托管JAR时项目无法编译——这也是我用stub做编译依赖的原因。

现在想知道为什么测试时无法识别非托管JAR?


解答

问题核心是SBT中Provided作用域对测试类路径的处理逻辑:

  1. Provided依赖仅会排除Runtime作用域,但测试类路径默认包含Compile和Test作用域的依赖,stub作为Provided依赖依然会被加入测试类路径,且因为是本地项目编译产物,类路径优先级比/lib下的非托管JAR更高,导致测试时优先加载了stub的未实现类。
  2. 而runMain使用的是Runtime作用域,Provided依赖会被排除,所以能正常加载/lib里的JAR。

解决方法

修改MainClass项目配置,让stub仅在编译阶段生效,同时确保测试类路径排除stub,保留非托管JAR:

lazy val stub = project
  .in(file("stub"))
  .settings(
    name := "stub"
  )

lazy val MainClass = project
  .in(file("MainClass"))
  .settings(
    name := "MainClass",
    libraryDependencies += "org.scalatest" %% "scalatest" % "3.2.14" % Test,
    // 仅在编译时引入stub的产物
    Compile / unmanagedClasspath += (stub / Compile / packageBin).value,
    // 测试类路径过滤掉stub的JAR,保留lib下的实际实现
    Test / unmanagedClasspath ~= { cp => 
      cp.filterNot(_.data.getName.startsWith("stub_")) 
    }
  )

原理说明

  • 直接将stub的编译产物加入编译类路径,保证无实际JAR时项目可正常编译。
  • 在测试类路径中过滤掉stub的产物,确保测试时JVM优先加载/lib目录下的实际实现JAR。

这样既满足了编译依赖需求,又能让测试和运行阶段使用真实的JAR实现。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 05:55:19