避免在测试文件中实例化对象

Avoiding to instantiate object in test files

我有一个 Redis PubSub 对象:

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
  }
}

问题是当我 运行 在 Jenkins 中测试时,我不需要实例化这个对象。因此,我的测试失败了,因为我试图连接到我在本地主机上使用的 redis 服务器(在 application.conf 中设置)。我只是想在我的测试中忽略这个对象,因为无论如何,我不使用它。

我如何在测试文件中实例化应用程序:

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

其中 StartUpService 是单例 class。但是对于对象,我不能以这种方式禁用它。

[[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)

感谢

Solution

根据@Tim 的回复,我发现 module class 在应用程序中实例化了一些对象

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

由于 StartUpService class,我实例化了很多服务,所有这些都在我构建应用程序对象的所有测试中触发。解决方案是创建一个 TestModule(没有 StartUp class)并用它覆盖第一个。

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

现在我可以在不触发所有服务的情况下创建应用程序对象,如:

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

object 仅在访问 object 的成员时初始化,因此您必须在测试中至少使用 RedisPubSubService 中的一个值。

干净的解决方案是将测试中使用的所有值移动到单独的 class(RedisPubSubConfig?)中,并且只有 RedisPubSubService 中有真正的服务代码。这样服务只会在使用时创建。

测试可以使用单独的 objectRedisPubSubMock?)中的 Mock 服务,该服务也使用 RedisPubSubConfig。此 object 中的代码只会在测试期间初始化。

或者,您可以制作相关的 vals lazy,这样它们只会在首次使用时被初始化。