1

WS (Web サービス) と呼ばれる (インポートされた) シングルトン インスタンスに依存するメソッドをテストしているとします。このインスタンスには、URL を受け取ってリクエストを返す メソッドurl(url: String)があります。

def doRequest(url: String): Future[Response] = {
  val request = WS.url(url)
  for {
    response <- request.post(params)
  } yield {
    val res: JsResult[MyResult] = response.json.validate[MyResult]
    res.getOrElse(throw new NotSupportedException)
  }
}

単体テストで実際のアウトバウンド http 要求を必要とせず、代わりにモック WS インスタンスに依存できるように、WS 依存関係を注入できるようにしたいと考えています。

シングルトンには技術的に型 (Class[WS.type]) がありますが、Class[WS.type] を期待する val にシングルトンをバインドすると、WS のプロパティとメソッドが失われるため、これは私にとって課題でし。これは、次のように単純なケーキ パターンを単純に使用できないことを意味します。

trait WSComponent {
  val ws: Class[_ <: WS.type]
}

object ApplicationContext extends WSComponent {
  val ws = WS
}

object TestContext extends WSComponent {
  val ws = mock[WS]
}

これを実行してから、いずれかのコンテキストで WS のメソッドを呼び出すと、Class[_ <: WS.type] には (たとえば) url() と呼ばれるメソッドがないというコンパイル エラーが発生します。

同様の理由のように思えますが (基本的に、シングルトン オブジェクトには型がありませんが、型はありません)、WS.type を受け取る暗黙のパラメーターを指定することはできません。シングルトン オブジェクトで宣言されたプロパティ。

シングルトン オブジェクトに依存関係を注入するには、どのようなアプローチがありますか? 私は DI にケーキ パターンを使用することを楽しんでいますが、コードにかなりのボイラープレートが導入されるため、理想的なソリューションはコードにボイラープレートを追加しすぎないことです。

ご提案いただきありがとうございます。

4

2 に答える 2

1

シングルトン オブジェクトには型があり、それらのメソッドを呼び出すことができます。

scala> val i: Int.type = Int
i: Int.type = object scala.Int

scala> i.box(42)
res0: Integer = 42

あなたのエラーが関係していると思います

val ws: Class[_ <: WS.type]

以下で実装されています:

val ws = WS

それはコンパイルできず、実際にClass[...]はメソッドもありませんurl()ws次のように入力するだけですWS.type

trait WSComponent {
  val ws: WS.type
}

そして、モックを に変更しmock[WS.type]ます。


編集:以下の他の方法は、タイプを制御できる場合にのみ機能しますWS(明らかに、ここではそうではありません。それは遊びから来ているからです)

シングルトン型を本当に避けたい場合はWS、シングルトン実装のトレイトになり、ケーキのトレイトのみを参照することができます。

trait WS {
  def url(url: String): Request
}

object SingletonWS extends WS {
  def url(url: String) = ??? // actual implementation
}

そしてあなたのケーキで:

trait WSComponent {
  val ws: WS
}

object ApplicationContext extends WSComponent {
  val ws = SingletonWS
}

object TestContext extends WSComponent {
  val ws = mock[WS]
}
于 2013-06-20T19:19:52.043 に答える
0

使用するからの呼び出しを含むトレイトを定義してからWS、 に委譲する単純なラッパー impl を定義してみてくださいWS。このようなもの:

trait WSMethods{
  def url(str:String):Request
}

object WSWrapper extends WSMethods{
  def url(str:String) = WS.url(str)
}

次に、それを必要とするクラスに混合するトレイトで次のように使用します。

trait WSClient{
  val ws:WSMethods
}

それを行うと、より嘲笑的になります。少し面倒ですが、オブジェクトがその操作を定義する特性に混ざっていない場合は、このようになります。タイプセーフの人々がWSそれを行っていれば、嘲笑するのは簡単でしょう。また、ほとんどのモック フレームワーク (おそらくすべて) は、次のようなことを試してみると barf します。

val m = mock[WS.type]
于 2013-06-20T19:36:18.613 に答える