Scala隐式类型依赖注入

时间:2013-01-15 01:36:25

标签: scala dependency-injection akka typeclass

我想帮助整理这个场景。我有一个Akka演员,我想注入一个依赖,在这种情况下RemoteFetcher,我也想在我的测试中模拟。像这样:

主/ SRC /阶/的 mypackage的/ Services.scala

package mypackage
import RemoteFetcherFileSystem._

trait RemoteFetcher {
  def fetch( path:String ): Future[Stream[String]]
}

class MyRemoteResourceActor extends Actor with ActorLogging {
  def fetchRemote( path:String ) = implicitly[RemoteFetcher].fetch( path )
  def receive = {
     case FetchRemoteResource( path ) => fetchRemote( path ).map( _.foreach( sender ! _ ) )
  }
}

为此,我有一个隐含的对象,我导入上面的文件。看起来像这样:

implicit object RemoteFetcherFileSystem extends RemoteFetcher {
   def fetchRemote( path:String ) = Future[Stream[String]] { ... reading from file system ... }
}

现在在我的测试中,我有来自akka-testkit的TestActor。在这里,我想改为导入我的模拟依赖:

implicit object RemoteFetcherMock extends RemoteFetcher {
   def fetchRemote( path:String ) = Future[Stream[String]] { ... mock implementation ... }
}

我的问题是要编译Services.scala我需要导入隐式对象。但是我如何在我的测试文件中影响/覆盖它。我没有使用隐式参数的原因是我想避免修改我的所有actor构造函数参数。

当我环顾四周并阅读类型类依赖注入模式时,我按照教程开始工作,但是当我想在我的例子中进行测试和覆盖时,我不能让它工作。< / p>

2 个答案:

答案 0 :(得分:1)

我不确定如何使用implicits,但通常可以像这样注入:

trait RemoteFetcherComponent {
  def remoteFetcher: RemoteFetcher
  trait RemoteFetcher {
    def fetch(path: String): Future[Stream[String]]
  }
}

trait RemoteFetcherFileSystemComponent extends RemoteFetcherComponent {
   val remoteFetcher = RemoteFetcherFileSystem
   object RemoteFetcherFileSystem extends RemoteFetcher {
     def fetch(path: String): Future[Stream[String]] = ???
   }
}

class MyRemoteResourceActor extends Actor with ActorLogging with RemoteFetcherFileSystemComponent {
  def fetchRemote(path: String) = remoteFetcher.fetch(path)
  def receive = {
     case FetchRemoteResource(path) => fetchRemote(path).map( _.foreach(sender ! _))
  }
}

val myRemoteResourceActor = new MyRemoteResourceActor()

然后测试值将如此定义:

trait RemoteFetcherMockComponent extends RemoteFetcherComponent {
  def remoteFetcher = RemoteFetcherMock
  object RemoteFetcherMock extends RemoteFetcher {
    def fetch(path: String): Future[Stream[String]] = ???
  }
}

val myMockedResourceActor = new MyRemoteResourceActor with RemoteFetcherMockComponent {
  override val remoteFetcher = super[RemoteFetcherMockComponent].remoteFetcher
}

您遇到隐含问题的原因是因为您使用它的方式与仅使用def fetchRemote(path: String) = RemoteFetcherFileSystem.fetch(path)没有区别。通过导入,您已经定义了实现,而不是允许稍后注入。

答案 1 :(得分:0)

您还可以将implicitly更改为隐式参数:

trait RemoteFetcher {
  def fetch(path: String): Future[Stream[String]]
}

object RemoteFetcher {
   implicit val fetcher = RemoteFetcherFileSystem
}

class MyRemoteResourceActor extends Actor with ActorLogging {
  def fetchRemote(path: String)(implicit remoteFetcher: RemoteFetcher) = remoteFetcher.fetch(path)
  def receive = {
     case FetchRemoteResource(path) => fetchRemote(path).map( _.foreach(sender ! _))
  }
}

然后,您只需导入RemoteFetcher即可覆盖在RemoteFetcherMock的配套对象中解析的隐式。

有关隐式参数解析优先级规则的详细信息,请参阅this post