0

I am doing a web application with Scala and Akka actors and I'm having some troubles with the tests.

In my case I need to taste an actor who talks with the Database. To do the unit testing I would like to use a Fake Database but I can't replace the new with my desired fake object.

Let's see some code:

Class MyActor extends Actor {
    val database = new Database()

    def receive = { ... }
}

And in the tests I would like to inject a FakeDatabase object instead Database. I've been looking in Internet but the best that I found is:

  • Add a parameter to the constructor.
  • Convert the val database to a var so in the test I could access the attribute by the underlying and replace it.

Both solutions solve the problem but are very dirty.

Isn't a better way to solve the problem?

Thanks!

mtrebi
  • 267
  • 1
  • 5
  • 19
  • 2
    I wouldn't call "adding a parameter to the constructor" dirty. If you think about the semantics: can the actor function without a DB? Probably not, so a constructor is a very good place for it because you're explicitly stating that for the actor to be created it needs a database passed. – Akos Krivachy Apr 07 '14 at 20:12
  • I'm not an expert, but in your test could you override the database like this: new MyActor { override val database = new FakeDatabase } – user24601 Apr 08 '14 at 15:43
  • I think that it could work and it's the simplest solution. Thanks – mtrebi Apr 09 '14 at 14:44

2 Answers2

0

The two primary options for this scenario are:

  1. Dependency Injection Use a DI framework to inject a real or mock service as needed. In Akka: http://letitcrash.com/post/55958814293/akka-dependency-injection

  2. Cake Pattern This is a Scala-specific way of achieving something akin to dependency injection without actually relying on injection. See: Akka and cake pattern

Community
  • 1
  • 1
Ryan
  • 7,227
  • 5
  • 29
  • 40
0

Echoing the advice here, I wouldn't call injecting the database in the constructor dirty. It might have plenty of benefits, including decoupling actor behaviour from the particular database instance.

However if you know there is only ONE database you will be always using in your production code, then think about defining a package level accessible constructor and a companion object returning a Props object without parameters by default.

Example below:

object MyActor {

    def props() : Props = Props(new MyActor(new Database()))

}

class MyActor private[package](database : IDatabase) extends Actor {

    def receive = { ... }
}

In this case you will still be able to inject the test database in your tests case (given the same package structure), but prevent users of your code from instantiating MyActor with unexpected database instance.

Norbert Radyk
  • 2,608
  • 20
  • 24
  • I'm pretty sure that we will have more than one database. I was also trying to avoid the parameters in the constructor for others reasons in my code that I did not comment here to simplify the problem. Anyway I will take a look. Thanks @Norbert – mtrebi Apr 07 '14 at 20:51