2013-01-15 146 views
2

我想要一些幫助來整理這個場景。我有一個Akka actor,我想要注入一個依賴項,在這個例子中是RemoteFetcher,我也想在我的測試中模擬它。像這樣:Scala隱式類依賴注入

主/ 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構造函數參數。

我環顧四周,閱讀了類型依賴注入模式,並根據教程讓它工作,但當我想在我的示例中進行測試和覆蓋時,我沒有得到它的工作。

回答

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)沒有什麼不同。通過導入,您已經定義了實現,而不是稍後允許它被注入。

+0

所以我想你建議在這裏使用蛋糕模式?那麼值得一試。我在教程中看到它非常詳細,但看起來不錯。 – Magnus

+0

我添加了另一個答案,用implicits來實現。也許不是你想要它,或者最好的方式,但它絕對是一個起點。 –

+0

我想你的意思是讓演員擴展RemoteFetcherFileSystemComponent? – Magnus

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 ! _)) 
    } 
} 

然後,你可以通過導入RemoteFetcherMock重寫處於RemoteFetcher同伴對象解決了隱。有關隱式參數解析優先規則的更多信息,請參閱this post

+0

我試過了。但由於某種原因,它並沒有真正解決我的問題,因爲我得到了隱含的模棱兩可的碰撞(也許是REPL在玩弄我)。我想這也是一種選擇。我想我最終會選擇蛋糕模式。 – Magnus