Search code examples
scalascalatest

Avoiding to instantiate object in test files


I have a Redis PubSub object:

object RedisPubSubService {
  implicit val system = ActorSystem("RedisEtatyLib")

  val config = ConfigFactory.load.getObject("redis").toConfig
  val hostname = config.getString("master")
  val port = config.getInt("port")
  val client = RedisClient(hostname, port)

  val address: InetSocketAddress = new InetSocketAddress(hostname, port)
  val channels = Seq("DASHBOARDS")
  val patterns = Seq("pattern.*")
  var callback: Boolean => Unit = { m => }

  val subscriber = system.actorOf(Props(classOf[SubscribeActor], address, channels, patterns, callback)
    .withDispatcher("rediscala.rediscala-client-worker-dispatcher")
  )

  def publish(channel: String, msg: String) =
     client.publish(channel, msg)
}

class SubscribeActor(address: InetSocketAddress,
                     channels: Seq[String] = Nil,
                     patterns: Seq[String] = Nil,
                     callback: Boolean => Unit)
  extends RedisSubscriberActor(address, channels, patterns, Option(""), callback) {

  def onMessage(message: Message): Unit = {
    // stuff
  }
}

The problem is that I don't need this object to be instantiated when I run tests in Jenkins. Due of that, my tests failed because are trying to connect to a redis server which I use on localhost (is setted in application.conf). I just want to ignore this object in my tests, because anyway, I don't use it.

How I instantiate the app in test file:

val app = new GuiceApplicationBuilder().disable[StartUpService].build()

where StartUpService is a singleton class. But fo objects, I can not disable it in this way.

[[37minfo[0m] p.a.i.l.c.CoordinatedShutdownSupport - Starting synchronous coordinated shutdown with ServerStoppedReason reason and 2147533000 milliseconds timeout [[37minfo[0m] p.c.s.AkkaHttpServer - Terminating server binding for /0.0.0.0:9001 [[37minfo[0m] p.c.s.AkkaHttpServer - Running provided shutdown stop hooks CONNECTING COMMAND FAILED, cause = Some(java.net.ConnectException: Connection timed out)

thx

Solution

Based on @Tim response, I found that the module class instantiate some object in the app

class Module extends AbstractModule {
  override def configure() = {
    bind(classOf[StartUpService]).asEagerSingleton
    bind(classOf[TraitRepository]).to(classOf[ClassRepository])
  }
}

Due of StartUpService class, where I instantiate a lot of services, all these are fired in all tests where I build the app object. The solution was to create a TestModule (without StartUp class) and use it in overriding the first.

object TestModule extends AbstractModule {
  override def configure() = {
    bind(classOf[TraitRepository]).to(classOf[ClassRepository])
  }
}

And now I can create the app object without firing all the services, as:

val app = new GuiceApplicationBuilder()
        .disable[Module]
        .overrides(TestModule)
        .build()

Solution

  • An object is only initialised when a member of the object is accessed, so you must be using at least one value in RedisPubSubService in your tests.

    The clean solution is to move all the values used in the tests into a separate class (RedisPubSubConfig?) and only have the real service code in RedisPubSubService. That way the service will only be created when it is used.

    The tests can use a Mock service that is in a separate object (RedisPubSubMock?) that also uses RedisPubSubConfig. The code in this object will only be initialised during the tests.

    Alternatively you can make the relevant vals lazy so they are only intialised when they are first used.