ScalaのDIにMacWireを使う

ScalaでのDIはいくつもやり方があります。CakePatternを使った方法や、ImplicitParameterやReaderMonadを使った方法などです。javaっぽくGoogleGuiceみたいなDIコンテナを使うこともできます。

今回は[数あるScalaのDIライブラリ](https://www.playframework.com/documentation/2.2.x/ScalaDependencyInjection#Example-Projects)の中でも、コンストラクタDIを簡単に書ける[MacWire](https://github.com/adamw/macwire)というものを紹介します。

## MacWireとは

詳細な説明は[ドキュメント](http://di-in-scala.github.io/)があります。
簡単に言うと、

- コンストラクタDI最高。
- コンパイル時に依存はチェックしたい。
- 依存関係書くの辛いからマクロで解決。

という感じで、マクロでDIの依存関係を解決してくれます。

## MacWireなしのコンストラクタDIの例

```scala:MacWireなし
trait DbModule {
  lazy val db = new DB("localhost", 3306)
  lazy val userDao = new UserDao(db)
}
```

## MacWireありのコンストラクタDIの例

```scala:MacWireあり
import com.softwaremill.macwire._

trait DbModule {
  lazy val db = new DB("localhost", 3306)
  lazy val userDao = wire[UserDao]
}
```

テストの時は同じですが、↑のようにwireを使うことで依存を書く必要がなくなります。

実際はマクロで`new UserDao(db)`に展開されます。

使うときはmix-inして使えばokですね。

テスト用に、mockにしたModuleにしてみます。

```scala:Mockに
trait DbModuleMock {
  override lazy val db = mock[DB]
}
```

そしてテストでもmix-inしたりして使えばいいですね。

```scala
class UserDaoSpec extends DbModuleMock {
  "UserDao" should {
    "insert" it {
       userDao.insert(1)
       verify(db).insert(1) // mockになってるので呼ばれたかを確認してる
    }
  }
}
```

ここまでが簡単な例でした。

## 少し複雑な依存の例

依存が増えた時に、このwireはとても便利なものになります。

```scala:MacWireなし
trait DbModule {
  lazy val db = new DB("localhost", 3306)
  lazy val userDao = new UserDao(db)
  lazy val userIdGen = new UserIdGen()
  lazy val kvs = new Kvs("localhost", 6379)
  lazy val userCache = new UserCache(kvs)
  lazy val userService = new UserService(userIdGen, userCache, userDao)
}
```

dbのアクセスをkvs経由にしてcacheするみたいなイメージです。

これはwireで書くと

```scala:MacWireあり
trait DbModule {
  lazy val db = new DB("localhost", 3306)
  lazy val userDao = wire[UserDao]
  lazy val userIdGen = wire[UserIdGen]
  lazy val kvs = new Kvs("localhost", 6379)
  lazy val userCache = wire[UserCache]
  lazy val userService = wire[UserService]
}
```

とてもシンプルで良いですね。

テスト時はmockにしたModuleに。

```scala:MacWireあり。テスト時
trait DbModuleTest extends DbModule {
  override lazy val db = mock[DB]
  override lazy val kvs = mock[Kvs]
}
```

このようにシンプルなので学習コストはないに等しく、簡単にコンストラクタDIができ最高ですね。

## テストの時だけFutureを同期実行する

テストの時だけFutureは同期的にしたいことは良くあります。やり方は、overrideや[抽象的なFuture](http://eed3si9n.com/ja/the-abstract-future)等いくつかあると思います。

DIを使ってテストの時だけ同期実行にしてみます。

まず非同期に実行するためのクラスを定義します。

```scala:Async
class Async {
  def apply[A](f: => A)(implicit ec: ExecutionContext): Future[A] = Future(f)
}
```

それをDIして使います。

```scala:UserService
import ExecutionContext...

class UserService(userDao: UserDao, async: Async) {
  def insert(name: String): Future[Long] = async {
    userDao.insert(name)
  }
}
```

```scala:DbModule
trait DbModule {
  lazy val db = new DB("localhost", 3306)
  lazy val async = wire[Async]
  lazy val userDao = wire[UserDao]
  lazy val userService = wire[UserService]
}
```

テストの時は同期的にします。

```scala:DbModuleTest
trait DbModuleTest extends DbModule {
  override lazy val db = mock[DB]
  override lazy val async = new Async {
    override def apply(f) = Future.successul(f) // テスト時だけ同期的にする
  }
}
```

こうすれば、asyncを使うように統一することでテスト時は全部同期実行にできます。

## おまけ

https://github.com/adamw/macwire

下のほうに載ってますが、Tagged typeに対応したり、Playでの使用例が書いてあったり、Implicitでもwireできたりしますね。

いくつかサンプルプロジェクトあったりもして、ドキュメントが充実してて良いですね。

おわり(*´ο`*)=3