Search code examples
scalasbtsbt-assembly

How to include test dependencies in sbt-assembly jar?


I am unable to package my test dependencies in my test assembly jar. Here is an excerpt from my build.sbt:

...

name := "project"

scalaVersion := "2.10.6"

assemblyOption in (Compile, assembly) := (assemblyOption in (Compile, assembly)).value.copy(includeScala = false)

fork in Test := true

parallelExecution in IntegrationTest := false

lazy val root = project.in(file(".")).configs(IntegrationTest.extend(Test)).settings(Defaults.itSettings: _ *)

Project.inConfig(Test)(baseAssemblySettings)

test in (Test, assembly) := {}

assemblyOption in (Test, assembly) := (assemblyOption in (Test, assembly)).value.copy(includeScala = false, includeDependency = true)

assemblyJarName in (Test, assembly) := s"${name.value}-test.jar"

fullClasspath in (Test, assembly) := {
  val cp = (fullClasspath in Test).value
  cp.filter{ file => (file.data.name contains "classes") || (file.data.name contains "test-classes")}  ++  (fullClasspath in Runtime).value
}

libraryDependencies ++= Seq(
  ...
  "com.typesafe.play" %% "play-json" % "2.3.10" % "test" excludeAll ExclusionRule(organization = "joda-time"),
  ...
)

...

When I assemble my fat jar using sbt test:assembly, is produces the fat jar project-test.jar, but the play-json dependencies aren't being packaged in:

$ jar tf /path/to/project-test.jar | grep play
$

However, if I remove the "test" configuration from the play-json dep (i.e. "com.typesafe.play" %% "play-json" % "2.3.10" excludeAll ExclusionRule(organization = "joda-time")), I can see it being included:

$ jar tf /path/to/project-test.jar | grep play
...
play/libs/Json.class
...
$

Am I doing anything wrong and/or missing anything? My goal here is to include the play-json library in ONLY the test:assembly jar and NOT the assembly jar


Solution

  • I have left out a crucial part in the original build.sbt excerpt I posted above which turned out to be the cause of the issuse:

    fullClasspath in (Test, assembly) := {
      val cp = (fullClasspath in Test).value
      cp.filter{ file => (file.data.name contains "classes") || (file.data.name contains "test-classes")}  ++  (fullClasspath in Runtime).value
    }
    

    This code block was essentially filter out deps from the test classpath. We include this to avoid painful merge conflicts. I fixed this by adding logic to include the play-json dep that was needed:

    fullClasspath in (Test, assembly) := {
      val cp = (fullClasspath in Test).value
      cp.filter{ file =>
        (file.data.name contains "classes") ||
        (file.data.name contains "test-classes") ||
        // sorta hacky
        (file.data.name contains "play")
      }  ++  (fullClasspath in Runtime).value
    }