Wednesday, 13 January 2010

Using SBT on your Scala Maven project for continous testing

Continuous testing is the practice of rerunning test cases whenever you change source code. Its a great way to write code in a mostly test-first way and it gives you very rapid feedback as you change code.

It can take a while to rebuild & rerun tests on a Scala project when using Maven (and IDEs can be a little slow sometimes too when it comes to continuous testing and scala).

You can buy Infinitest as an IDE plugin which is triggered whenever you do a build. Though I've found the console view isn't great when running ScalaTests (there's no way to navigate to the code that fails) - and you have to explicitly hit 'build' when you're editing your code. I prefer tests to just rerun automatically (reusing IDEA's auto-save feature).

So another approach is to use sbt, the simple build tool for scala.

Installing sbt
I use 0.6.9 (or xsbt as its often called) so I can work with scala 2.8.0.Beta1-RC7 nicely. Basically download the jar and create script called 'sbt' or 'xsbt' if you prefer, then you can run sbt from the command line.

You can then run sbt in the directory of your pom.xml and it'll configure itself with whatever project & version you want to use and version of sbt/scala etc.

Setting up sbt for maven projects
For those new (like me) to sbt, here's a quick way to get your maven build converted to use sbt. If you don't have a maven build for your project you can skip this step.

To cut corners and not configure all the maven repos you want to use to download your dependencies, you can just let sbt reuse your local maven repo to find jars.

So create a scala file in the project/build directory. sbt will have created the project directory for you, along with a boot subdirectory but you will need to create the build directory.

Call the file whatever you like, such as project/build/Foo.scala...
import sbt._

class FooProject(info: ProjectInfo) extends DefaultProject(info) {

val mavenLocal = "Local Maven Repository" at
"file://"+Path.userHome+"/.m2/repository"
}
Now you can start sbt and update it to load all the maven dependencies from your project

sbt
update
compile

The first execution of sbt puts you in the sbt shell which has completion and help so you can find the various options available. The 'update' task loads & downloads all the dependencies from your pom.xml. Refer to the docs for more details on repositories and dependencies.

Now you've got your dependencies imported, lets do some continuous testing.

Continuous Testing
From the sbt shell just type
~ test
Now sbt will monitor your code, detect when its changed, rebuild and rerun all the tests for you showing the results in nicely colour coded output in the terminal.

When you fancy playing in the Scala interactive shell (REPL) just type

console-project

And you're good to go. Enjoy!

Have you figured out a neater way of doing continuous testing with IDEA and Scala?

I guess someone could hack a JRebel plugin for Maven so that you could use the scala:cc goal to incrementally compile your scala code as you edit it; then JRebel automatically reloads the changed classes, then the new continuous testing plugin would just need to use a trigger in the JRebel SDK so that when a class is reloaded it reruns the relevant test cases (or all of them - maybe sorted by previously failed ones first).

A similar technique works great when running projects with jetty:run or scala:console - letting JRebel reload any classes rebuilt via scala:cc

9 comments:

Daniel said...

Calling "console" instead of "console-project" gives you access to the test definitions as well.

James Strachan said...

Cool thanks!

For some reason console barfs for me when I exit it to return to the sbt shell (as well as hogging lots of CPU).

Seems it doesn't quite work perfectly wit 0.6.9 and Scala 2.8.0 beta RC7 (though its probably fine on earlier sbt/scala combos)

e.g. I get massive recursive calls to...

at scala.tools.nsc.symtab.Scopes$Scope.enterInHash(Scopes.scala:145)

James Strachan said...

What would be awesome BTW is if IDEA could watch sbt running and visualise the test run information in its usual JUnit runner UI - then you can click on tests & see their results & click on stack traces to navigate code etc.

I raised an issue for the Scala plugin...

http://youtrack.jetbrains.net/issue/SCL-1724

Christopher said...

Slightly related question: do you find yourself using Scala more than other languages nowadays?

I would be very interested to learn about what your ideal language and toolset choices are; perhaps another blog entry on this topic...

Thanks.

James Strachan said...

@Christopher I find myself using Java on projects which started off using Java that haven't switched yet - or when using GWT (my preferred approach for rich web applications). Though I wish there was a Scala version of GWT!

For anything else I try use Scala for the reasons I gave in a previous post

Steffen said...

Have you tried using this on a nested maven project?

And if yes how did you make it work?

When i use xsbt in my top project it does'nt detect the child modules sources and tests. When using xsbt in the modules it finds the source but fails to load the parent pom with sbt error java.io.IOException: Impossible to load parent for file:/

By the way really cool idea about using xsbt, I was really sad that we had to use maven instead of xsbt because of ejb and j2ee elements in our project.

Thanx in advance
Steffen

James Strachan said...

We've been using 0.6.12 of sbt on Scalate lately and that works great with multi-projects.

The one trick with sbt is always, make sure you do an 'update' first :)

Ittay Dror said...

I use sbt 0.7.4 and it does not detect child modules. Any hints?

James Strachan said...

You have to mention your sub modules in your Project scala class (see the Scalate project for example).

Maybe send your issue to the SBT mailing list for better help?