This post is about some of the different ways we can test our scala code. I think this is a bit of an overlooked subject, you don’t see too many scala tutorials that show you how you should test and its often quite different. At the surface this seems pretty basic, but like most code there are lots of ways to do it.

No mocking

You could test something by running it, and checking that it does what it should. This is the basic plan of testing. When it gets a bit more complicated you may want some strategies to help with this. To deal with breaking up and testing separate parts etc.

For example if you want to test saving something. Say you have your userRepositiory.

You can simply have a test like.
 import org.specs2.mutable.Specification  
import play.api.test.Helpers._
import play.api.test._
import play.api.Play.current
import play.api.db.DB
import anorm._
class UserSpec extends Specification {
“User model" should {
"find all Users" in new WithApplication {
DB.withConnection { implicit connection =>
SQL(“insert into Users (‘uname’)).execute()
val found = User.find()
found must equalTo Seq(User(“uname"))
}
}
}
}


This works fine and play framework gives you nice tools to be able to do this easily. I’d do this while its simple, but once you start getting many dependencies it can get messy.

mocking
Everyone is probably familiar with mockito, the java library for testing that uses reflection to define anonymous classes, and define what method calls should return.

You typically want to use this to stop the real action happening.

Consider the simple example you may want to test. Saving an entity to a database. You may want to avoid the actual saving, and check that your code still passes whatever data through.

 val mockRepo = mock[UserRepository]  
when(mockRepo.save(any[user])) thenReturn True
..
there was one(mockRepo.save(any[user]))

These work well for testing your logic around the mocked component. You should be sure that you have tested the mocked component too. You need to be careful it is possible that bugs can slip through here, for example if your data is going to break a constraint in the database.


trait overloading

You can define your code in a trait and use overloading to change it in test. e.g.

 trait TheAPI {  
def getMe: Future[User] = {
WS.get("www.api.3rdparty.com/users”).map { res =>
Json.reads[User](res.json)
}
}
}
val mockedAPI = new TheAPI {
overload getMe: Future[User] = Future(User(“me!!”))
}

When you do this, you need to do a bit more to compose your code so you can use it, similar to cake pattern.

cake pattern
There are more detailed explanations of cake pattern out there you can read, but the gist of it is to use traits cleverly for dependency injection. It can be used to inject different implementations for test.

Say you have a object like this.

 trait MegaController with UserRepo  
val prodMegaController = MegaController with DatabaseUserRepo
val testMegaController = MegaController with TestUserRepo
trait UserRepo {
def find: Option[User]
def save(u: User): Unit
}

This is great because its just using a language feature, everything is type safe and static. As opposed to mockito where it makes objects for everything.


Play framework plugins
Another option, if you are using the play framework is to put the dependencies into plugins and inject test plugins into unit tests.

 class SomeApi(app: Application) extends Plugin {  
def doIt: String = ???
}
class TestAPI(app: Application) extends SomeApi(app) {
override def doIt = “test"
}
object SomeController extends Controller {
val someApi = Play.application.plugin[SomeApi]
.getOrElse(throw new RuntimeException(“SomeApi not loaded”))
def doIt = ???
}
“the API user” should {
val fakeApp = FakeApplication(
withoutPlugins = Seq(“SomeApi”),
additionalPlugins = Seq(“TestApi")
)
“do something with it” in running(fakeApp){
SomeController.doIt
}
}


functional separation.

My examples so far have been pretty simple.
You all know how code gets complicated anyway, and see whats going to happen, but lets consider a this example that makes suddenly some of these techniques have a lot of merrit.

Consider the following example which is slightly more complicated, it has nested uses of third party API’s, with some logic.

 object CrazyJob {  
def doit: Unit {
val a = WS.get('api.3rdparty.com/crazyUrl')
a.map { data1 =>
val sensibleData = MakeSenseOf(data1)
val data2 = if (isEligable(sensibleData)) {
OurSecretSource.addSpice(sensibleData)
} else sensibleData
WS.post('api.3rdparty.com/whackyUrl', data2).map { resp =>
if (resp.status != 200){
log.error("it didnt work :("+resp)
}
}
}
}
}

To test that as it is you’d need to mock both 3rd parties, which is fine and can be done easily. However it might be easier to split the code up and test the pieces independently, or even only test some of it. Its good to be able to test the detail at the level, rather than having lots of big integration tests exercising all of the logic.

 object CarzyJob {  
def doit: Unit {
val in = getData()
val transformed = transform(in)
val send(transformed)
}
def getData = {
val a = WS.get('api.3rdparty.com/crazyUrl')
}
def send(data: A) = {
WS.post('api.3rdparty.com/whackyUrl', data2).map { resp =>
if (resp.status != 200){
log.error("it didnt work :("+resp)
}
}
}
def transform(in: Seq[A]) = {
if (isEligable(in)) {
OurSecretSource.addSpice(in)
} else in
}
}


Futures
When your working with futures be careful to wait for them in your tests. Even if your using Future.successful in a mock and your just testing a side effect. Not waiting can lead to random test failures.
 val future = testCtrl.makeUser(User("Bill"))  
future.map { res =>
res.isSuccess
}.await

Conclusion

There are many ways to test code, be familiar with them, use them in the right context and enjoy writing tested maintainable code.