LoginSignup
12
12

More than 5 years have passed since last update.

Play2.4のDIベースなアプリのテスト実行について

Posted at

はじめに

以下のような依存関係のあるアプリがあって、ApplicationにはMyServiceが、MyServiceにはMyRepositoryが注入されているとする。

Application(Controller) -> MyService -> MyRepository

この時、MyServiceのテストをしたいと思った時、Applicationは不要なわけで、となるとさて、どうやってテスト対象のMyServiceを得るんだっけ?となったのが今回のポストのきっかけ。

※なお、実行環境によって本番用モジュールとテスト用モジュールを切り替えてテストする方法については以下にまとめています。
Play2.4のDIについて動作確認(Guiceの使い方)

動作確認

ベースプログラム

まず、今回使うプログラム群を示す。
ControllerであるApplication.scalaは activator new で生成できるテンプレのやつをベースにして、 service.run() を実行する。

Application.scala
@Singleton
class Application @Inject() (service: MyService) extends Controller {
  def index = Action {
    println(service.run())
    Ok(views.html.index("Your new application is ready."))
  }
}

MyServiceは

MyService.scala
@ImplementedBy(classOf[MyServiceForProduction])
trait MyService {
  def run(): String
}

@Singleton
class MyServiceForProduction @Inject() (@Named("production-rep") repository: MyRepository) extends MyService {
  def run() = "production: " + repository.findAll().size
}

@Singleton
class MyServiceForTest @Inject() (@Named("test-rep") repository: MyRepository) extends MyService {
  def run() = "test: " + repository.findAll().size
}

MyRepository#findAllの戻り値型はちょっとサボってます。。

MyRepository.scala
trait MyRepository {
  def findAll(): Seq[Int]
}

@Singleton
class MyRepositoryForProduction extends MyRepository {
  def findAll() = Seq(1,2,3,4,5)
}

@Singleton
class MyRepositoryForTest extends MyRepository {
  def findAll() = Seq(-1)
}
MyRepositoryModule.scala
class MyRepositoryModule extends AbstractModule {
  override def configure(): Unit = {
    bind(classOf[MyRepository])
      .annotatedWith(Names.named("production-rep"))
      .to(classOf[MyRepositoryForProduction])

    bind(classOf[MyRepository])
      .annotatedWith(Names.named("test-rep"))
      .to(classOf[MyRepositoryForTest])
  }
}

最後にModule定義を追加。

application.conf
play.modules.enabled += "modules.MyRepositoryModule"

http://localhost:9000/ にアクセスすれば、コンソールに production: 5 と表示される。
無事、本番用のモジュール群が注入されていることが確認できた。

MyServiceをテストしたい

Controllerからじゃなくて途中のServiceからテストしたい、とした場合誰がMyServiceForTestの方をnewしてしかも依存性の注入まで行うのか、というのが当初よく分からなかった。
Guiceのマニュアルを読んでInjectorを使って生成する事を知った。以下、テストコード。

MyServiceSpec.scala
class MyServiceSpec extends FlatSpec with Matchers {

  "MyService" should "be MyServiceForTest" in {
    val injector = Guice.createInjector(
      new AbstractModule(){
        override def configure(): Unit = bind(classOf[MyService]).to(classOf[MyServiceForTest])
      },
      new MyRepositoryModule())

    val service = injector.getInstance(classOf[MyService])

    service.run() should be ("test: 1")
  }
}

これで無事、Controllerからじゃなくても途中のモジュールをテストできる事が分かった。

12
12
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
12