← 🏠 Home

The essential Scala build tool tutorial

Make a production app in Scala with TDD unit tests, modules, Java interop, and run it on Docker. Less than 30 minutes.

Published , last updated .

Once you've set up the prerequisites and have, simply follow the instructions below and let me know of any feedback in the feedback form! And if you'd like more content like this, subscribe.

Create a basic empty SBT project

mkdir -p new-project
cd new-project
mkdir -p project
echo 'sbt.version=1.4.9' > project/build.properties

Compile the project

sbt compile

Recompile on code change

sbt '~compile'

~ watches for code changes. Press Enter to exit.

Create a source file

Run this command in another terminal window.

mkdir -p src/main/scala/
cat > src/main/scala/SimpleApp.scala << EOF
object SimpleApp extends App {
  println("It works")
}
EOF

Run continuously

Exit the ~compile command by pressing Enter

sbt '~run'

Change source to verify it works

sed -i -e 's/works/definitely works/' \
      src/main/scala/SimpleApp.scala

Add ScalaTest

echo 'libraryDependencies += "org.scalatest" %%' \
       '"scalatest" % "3.2.6" % Test' >> build.sbt

Create a test

mkdir -p src/test/scala/
cat > src/test/scala/SimpleTest.scala << EOF
import org.scalatest.funsuite._
final class SimpleTest extends AnyFunSuite {
  def sum(a: Int, b: Int): Int = ???
  test("sum works for 1 + 2 = 3") {
    assert(sum(1, 2) == 3)
  }
}
EOF

Test continuously

Exit previous ~run command by pressing Enter

sbt '~test'

Make it pass

sed -i -e 's/???/a + b/' src/test/scala/SimpleTest.scala

Use Scala 2.13

echo 'scalaVersion in ThisBuild := "2.13.5"' >> build.sbt

Add the sbt-native-packager plugin

echo 'addSbtPlugin("com.typesafe.sbt" % '\
       '"sbt-native-packager" % "1.8.1")' \
       > project/plugins.sbt

Enable sbt-native-packager

echo 'enablePlugins(JavaAppPackaging)' >> build.sbt

Package the app

sbt 'show dist'

Run packaged app

unzip -o -d /tmp/my-project \
        ./target/universal/new-project-0.1.0-SNAPSHOT.zip
/tmp/my-project/new-project-0.1.0-SNAPSHOT/bin/new-project

Dockerize your app

sbt docker:publishLocal

Run the docker image

docker run new-project:0.1.0-SNAPSHOT

Create a submodule

echo 'lazy val fizzbuzz = project' >> build.sbt

Compile the submodule

sbt fizzbuzz/compile

Add ScalaTest to submodule

echo '  .settings(libraryDependencies += "org.scalatest"' \
       '%% "scalatest" % "3.2.6" % Test)' >> build.sbt

Continually test the submodule

sbt '~fizzbuzz/test'

Create a method in the submodule

mkdir -p fizzbuzz/src/main/scala/
cat > fizzbuzz/src/main/scala/Fizz.scala << EOF
object Fizz {
  def buzz(n: Int): String = ???
}
EOF

Create a test in the submodule

mkdir -p fizzbuzz/src/test/scala/
cat > fizzbuzz/src/test/scala/FizzTest.scala << EOF
import org.scalatest.funsuite._
final class FizzTest extends AnyFunSuite {
  test("fizzBuzz for 4 is 4") {
    assert(Fizz.buzz(4) == "4")
  }
}
EOF

Pass the test

sed -i -e 's/???/s"$n"/' \
      fizzbuzz/src/main/scala/Fizz.scala

Make the app depend on the submodule

echo 'dependsOn(fizzbuzz)' >> build.sbt

Use the submodule in the app

sed -i -e 's/".*"/Fizz.buzz(args.last.toInt)/'\
      src/main/scala/SimpleApp.scala

Run the app with arguments

sbt "run --- 123"

Explicitly name the project

echo 'name := "new-project"' >> build.sbt

Explicitly version the project

echo 'version := "0.2"' >> build.sbt

Add JSoup Java library to main project

echo 'libraryDependencies += "org.jsoup" % "jsoup"' \
       '% "1.13.1"' >> build.sbt

Print all BBC News headlines

mkdir -p src/main/scala/
cat > src/main/scala/SimpleApp.scala << EOF
import org.jsoup.Jsoup
import scala.jdk.CollectionConverters._
object SimpleApp extends App {
  val doc = Jsoup.connect("http://www.bbc.com/news").get()
  val newsHeadlines = doc
    .select(".gs-c-promo-heading__title")
    .asScala
    .map(_.text().trim)
    .filter(_.nonEmpty)
  newsHeadlines.foreach(println)
}
EOF
sbt run

For feedback, or to subscribe, go to the Feedback form or the Subscription form.