Search code examples
scalasbtscalatestscala.js

sbt cross project, shared dependencies for test example


I have a small project.

Where I have the following problem:

scalaTest needs to be added to all three dependency project (client, server, shared), otherwise the scalatest library is not accessible from all projects.

In other words, if I write

 val jvmDependencies = Def.setting(Seq(
   "org.scalaz" %% "scalaz-core" % "7.2.8"
 )++scalaTest)

then things work fine.

But if I don't write ++scalaTest into each three dependencies then it fails like this:

> test
[info] Compiling 1 Scala source to /Users/joco/tmp3/server/target/scala-2.11/test-classes...
[error] /Users/joco/tmp3/server/src/test/scala/Test.scala:1: object specs2 is not a member of package org
[error] import org.specs2.mutable.Specification
[error]            ^
[error] /Users/joco/tmp3/server/src/test/scala/Test.scala:3: not found: type Specification
[error] class Test extends Specification {
[error]                    ^
[error] /Users/joco/tmp3/server/src/test/scala/Test.scala:5: value should is not a member of String
[error]   "Test" should {
[error]          ^
[error] /Users/joco/tmp3/server/src/test/scala/Test.scala:6: value in is not a member of String
[error]     "one is one" in {
[error]                  ^
[error] /Users/joco/tmp3/server/src/test/scala/Test.scala:8: value === is not a member of Int
[error]       1===one
[error]        ^
[error] 5 errors found
[error] (server/test:compileIncremental) Compilation failed
[error] Total time: 4 s, completed Mar 18, 2017 1:56:54 PM

However for production(not test) code everything works just fine: I don't have to add 3 times the same dependencies (in this example autowire) to all three projects if I want to use a library in all three projects, it is enough to add it to only the shared project and then I can use that library from all three projects.

For test code, however, as I mentioned above, currently I have to add the same library dependency (scalaTest - below) to all three projects.

Question: Is there a way to avoid this ?

Settings.scala:

import org.scalajs.sbtplugin.ScalaJSPlugin.autoImport._
import sbt.Keys._
import sbt._

object Settings {
  val scalacOptions = Seq(
    "-Xlint",
    "-unchecked",
    "-deprecation",
    "-feature",
    "-Yrangepos"
  )

  object versions {
    val scala = "2.11.8"
  }

   val scalaTest=Seq(
      "org.scalatest" %% "scalatest" % "3.0.1" % "test",
     "org.specs2" %% "specs2" % "3.7" % "test")

   val sharedDependencies = Def.setting(Seq(
    "com.lihaoyi" %%% "autowire" % "0.2.6"
  )++scalaTest)

 val jvmDependencies = Def.setting(Seq(
   "org.scalaz" %% "scalaz-core" % "7.2.8"
 ))

  /** Dependencies only used by the JS project (note the use of %%% instead of %%) */
  val scalajsDependencies = Def.setting(Seq(
    "org.scala-js" %%% "scalajs-dom" % "0.9.1"
  )++scalaTest)
}

build.sbt:

import sbt.Keys._
import sbt.Project.projectToRef
import webscalajs.SourceMappings

lazy val shared = (crossProject.crossType(CrossType.Pure) in file("shared")) .settings(
    scalaVersion := Settings.versions.scala,
    libraryDependencies ++= Settings.sharedDependencies.value,
    addCompilerPlugin("org.scalamacros" % "paradise" % "2.1.0" cross CrossVersion.full)
) .jsConfigure(_ enablePlugins ScalaJSWeb)

lazy val sharedJVM = shared.jvm.settings(name := "sharedJVM")

lazy val sharedJS = shared.js.settings(name := "sharedJS")

lazy val elideOptions = settingKey[Seq[String]]("Set limit for elidable functions")

lazy val client: Project = (project in file("client"))
  .settings(
    scalaVersion := Settings.versions.scala,
    scalacOptions ++= Settings.scalacOptions,
    libraryDependencies ++= Settings.scalajsDependencies.value,
    testFrameworks += new TestFramework("utest.runner.Framework")
  )
  .enablePlugins(ScalaJSPlugin)
  .disablePlugins(RevolverPlugin)
  .dependsOn(sharedJS)

lazy val clients = Seq(client)

lazy val server = (project in file("server")) .settings(
    scalaVersion := Settings.versions.scala,
    scalacOptions ++= Settings.scalacOptions,
    libraryDependencies ++= Settings.jvmDependencies.value
  )
  .enablePlugins(SbtLess,SbtWeb)
  .aggregate(clients.map(projectToRef): _*)
  .dependsOn(sharedJVM)

onLoad in Global := (Command.process("project server", _: State)) compose (onLoad in Global).value
fork in run := true
cancelable in Global := true

Solution

  • For test code, however, as I mentioned above, currently I have to add the same library dependency (scalaTest - below) to all three projects.

    That is expected. test dependencies are not inherited along dependency chains. That makes sense, because you don't want to depend on JUnit just because you depend on a library that happens to be tested using JUnit.

    Although yes, that calls for a bit of duplication when you have several projects in the same build, all using the same testing framework. This is why we often find some commonSettings that are added to all projects of an sbt build. This is also where we typically put things like organization, scalaVersion, and many other settings that usually apply to all projects inside one build.