2

我需要一些帮助来解决这种情况。我有一个 Akka 演员,我想在其中注入一个依赖项,在本例中为 RemoteFetcher,我也希望在我的测试中模拟它。像这样:

main/src/scala/ 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 我需要导入隐式对象。但是我如何在我的测试文件中隐藏/覆盖它。我不使用隐式参数的原因是我想避免修改所有演员的构造函数参数。

当我环顾四周并阅读类型类依赖注入模式时,我根据教程让它工作,但是当我想像我的示例中那样测试和覆盖时,我没有让它工作。

4

2 回答 2

1

我不确定如何使用隐式来做到这一点,但通常可以像这样注入:

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). 通过导入,您已经定义了实现,而不是允许稍后注入。

于 2013-01-15T01:45:54.440 回答
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

有关隐式参数解析优先规则的更多信息,请参阅这篇文章

于 2013-01-15T02:10:10.940 回答