Posted on July 21, 2014
Scalatra Tutorial (Part 2): Logging, debugging, and tests
Part 1 of this tutorial is here
You can follow along by getting the code from github here: https://github.com/jieyangh/scalatra-sample-API
The git hash for part 2 is 32190b1ae5eb685f6a06eaae6cd5fa15d5cf23bd
Now that we have a barebones implementation of a web API, let’s start adding on functionality and utility.
Debugging
Remote debugging with IntelliJ makes life much easier. Here’s how to set it up. Open the project in IntelliJ and go to Run->Edit->Configurations. Choose Remote , hit the “+” button, and enter in a name for the configuration (I chose “ScalatraDebug” for simplicity). The default settings are fine and will set up a debug port on port 5005:
We will need to modify the SBT parameters accordingly, since it defaults to a different port. Go to File->Settings->Other settings and select SBT from the side nav. Under IDE Settings, change the VM parameters so that its listening on port 5005:
-Xmx512M -XX:MaxPermSize=256M -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=5005
Launch the application in the SBT console by typing “container:start” as we did before in part 1. Once the app is running, go to Run->Debug and select the remote debugging configuration that you previously created.
Logging
Any serious enterprise level application will need logging. Scalatra uses the logback framework by default, so that’s what we’ll use too. First we’ll need to add this dependency in the librarydependencies sequence in build.sbt:
"ch.qos.logback" % "logback-classic" % "1.0.1"
Next we will create a logback.xml under src/main/resources. Chances are if you’re reading this you’ve used logback before. If not, the file contents are fairly self explanatory. We can modify the pattern to suit our purposes, but including the date, thread, level, logger and actual message is a good a starting point as any:
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>Captain's Log Stardate %date{ISO8601}. [%thread] %level %logger %msg
</pattern>
</encoder>
</appender>
<root level="debug">
<appender-ref ref="STDOUT" />
</root>
</configuration>
Finally, we’ll need to utilize the logger in our code. Let’s add some logging to the GreetingController scala class. First let’s create the logger:
val logger = LoggerFactory.getLogger(getClass)
And now we just need to use our logger object to log things:
logger.info("We have entered a spectacular binary star system in the Kavis Alpha sector");
Again, pretty self explanatory. Here’s the full listing for the GreetingController:
package sampleApi.controllers
import org.scalatra.ScalatraServlet
import org.slf4j.{Logger, LoggerFactory}
class GreetingController extends ScalatraServlet {
val logger = LoggerFactory.getLogger(getClass)
get("/") {
logger.info("We have entered a spectacular binary star system in the Kavis Alpha sector");
"Hello world"
}
get("/:name") {
val name = params.getOrElse("name", "world")
"Hello " + name
}
}
Tests
Now we’ll want to add some automated testing. ScalaTest appears to be the go to test framework for Scala, and it provides support for Scalatra as well. This can be accomplished by adding a dependency for Scalatra ScalaTest in the libraryDependencies sequence in build.sbt:
"org.scalatra" %% "scalatra-scalatest" % "2.2.2" % "test"
Next we’ll want to create a test under src/test/scala. We’ll want our test class to extend the ScalatraSuite class, which allows us to use simple sentences to specify the expected behavior of our application. This is Behavior Driven Development (BDD). We also want our test class to have the “FunSuite” trait so that it can treat each test case as a function value. Note that in the official Scalatra testing example, the listed code didn’t compile for me, because it could not resolve “FunSuiteLike”. Replacing “FunSuiteLike” with “FunSuite” fixed the issue for me.
import org.scalatra.test.scalatest.ScalatraSuite
import org.scalatest.FunSuite
import sampleApi.controllers.GreetingController
class GreetingControllerTests extends ScalatraSuite with FunSuite {
addServlet(classOf[GreetingController], "/*")
test("simple get") {
get("/bob") {
status should equal (200)
body should include ("Hello bob")
}
}
}
The test code is also pretty straightforward. An HTTP get request for “/bob” should return a status code of 200, and the response message should include “Hello bob”. You can run the test in IntelliJ by right clicking anywhere on the test code, or by creating a test configuration in the IDE and running it via the Run menu.