playframeworkと僕とUnitTest

この記事は [Play framework 2.x Scala Advent Calendar 2013](http://www.adventar.org/calendars/114) の20日目の記事です。

6日ぶり2回目…

## playframeworkを使う上で、こんな感じでテスト書いてます的な

突然お昼に振られたので、書いてますｗｗ

https://twitter.com/seratch/status/413907638875734016

前回同様これからPlayframeworkを触りだすお隣チーム向けでしょうか？

## Controllerのテストをする

* 目的はWithApplication/WithServerから出来る限り逃げる

playのControllerをUnit Testするサンプルです

テスト系のドキュメントは2.1系から急に充実した感ありますので、2.0系以来その辺見てない〜という方は見るだけで幸せ(?)になれるかもしれません

http://www.playframework.com/documentation/2.2.x/ScalaTest ← ここの一番下のUnit Testing Controllers ですね
http://www.playframework.com/documentation/2.2.x/ScalaFunctionalTest ← 実際にはこっちの下の方の Testing a controllerかな？

上記のように書くことでControllerのメソッドをテストする際にWithApplication/WithServerからだいたい逃げられます ← すごく重要
なお、分離しても `play.api.Play.current`が必要〜みたいな状況にしてしまうとダメです
実行時に`RuntimeException: There is no started application`とか言われるときは、だいたいそれです
なんとか分離しましょう

ということで下に一応サンプル

なんかこう色々と突っ込みどころありますけど気にしないで

```scala
trait SampleController {
  this: Controller =>

  def hoge(x:Int) = Action.async { implicit request =>
    Future{
      val y = Form( "multi" -> optional(number)).bindFromRequest.fold(
        error => 0,
        optM => optM.getOrElse(2)
      )
      x * y
    }.map(res => Ok(Json.obj("res" -> res)))
  }
}
```

```scala
import org.specs2._

import play.api.test._
import play.api.test.Helpers._
import play.api.libs.json._
import play.api.mvc.Controller

class SampleControllerSpec extends Specification {
  def is = s2"""

### path parameterに2を渡す

optionパラメータなしなら ${E1ExpectVal.toString} を返す $e1
optionパラメータ10なら ${E2ExpectVal.toString} を返す $e2
optionパラメータに文字列なら ${E3ExpectVal.toString} を返す $e3

"""

  val baseExpectResult:JsValue = {
    val s =
      s"""
        |{
        |  "res" : 4
        |}
      """.stripMargin
    Json.parse(s)
  }

  val expectResultTransform = { res:Int => __.json.update( (__ \ 'res).json.put(JsNumber(res))) }

  val E1ExpectVal = 4
  val E2ExpectVal = 20
  val E3ExpectVal = 0

  // 以下全て成功する前提にしている
  val e1Expect = baseExpectResult.transform(expectResultTransform(E1ExpectVal)).get
  val e2Expect = baseExpectResult.transform(expectResultTransform(E2ExpectVal)).get
  val e3Expect = baseExpectResult.transform(expectResultTransform(E3ExpectVal)).get

  val target = {
    val c = new SampleController with Controller
    c.hoge(2)
  }

  def e1 = {
    val res = target(FakeRequest())
    (status(res) === OK).updateMessage(s => s"s : statusがおかしい").orThrow
    contentAsJson(res) === e1Expect
  }

  def e2 = {
    val res = target(FakeRequest(GET, "/?multi=10"))
    (status(res) === OK).updateMessage(s => s"s : statusがおかしい").orThrow
    contentAsJson(res) === e2Expect
  }

  def e3 = {
    val res = target(FakeRequest(GET, "/?multi=hoge"))
    (status(res) === OK).updateMessage(s => s"s : statusがおかしい").orThrow
    contentAsJson(res) === e3Expect
  }

}
```

長いけど全部載せました

もちろん普段はもっとちゃんと書いてますｗ

### 単にControllerのメソッドをUnitテストしたいのなら

実はtraitにする必要性は全くありません

[Action#apply](https://github.com/playframework/playframework/blob/2.2.1/framework/src/play/src/main/scala/play/api/mvc/Action.scala#L98)をみると、引数にRequestを受け取っていることがわかります

なので、

```scala
def hoge = Action { request => Ok}
```

をとりあえず動かしたかったら、

```scala
hoge(FakeRequest())
```

で動きます、いやった〜

POSTリクエストでJsonをdataとして渡したいとか、色々とあると思いますが、
[FakeRequest](https://github.com/playframework/playframework/blob/2.2.1/framework/src/play-test/src/main/scala/play/api/test/Fakes.scala#L30)をちら見すればとりあえずだいたい解決します

### じゃあなんでtraitにするの？

`object SampleController extends Controller` でいいじゃんという話ですけど、
例えば永続化層へのつなぎ込みとかが必要になった場合にtraitのほうが都合が良いです
→ 最初に上げた、`play.api.Play.current`から逃げる方法にもなります

その他、所詮traitなんで、上手に分離すると色々と使い回せる？

```scala

// routeからはこっちを参照
object Application extends Controller with SampleController {
  val hogeModel = HogeModel
}

// ロジックは分離
trait SampleController {
  this : Controller =>

  def hogeModel : HogeModel

  def hoge() = Aciton{request =>
    hogeModel.find(...

   // 略
}

class SampleControllerSpec ... {

  // test時はmockに差し替える
  val targetController = new SampleController extends Controller {
    def hogeModel = MockHogeModel
  }

  // 以下なんかテスト
}
```

cake pattern 使うなら使うでも良いと思いますが、この辺は割りとテキトーにやっています

個人的には上記の様な例だと、testはprojectを分けたくなります
分けることによってMock/StubをTestプロジェクトで使いまわしやすいと思うからです

階層構造的にはこのような感じでしょうか？

```text
.
├── build.sbt
└── modules
    ├── web
    │   ├── app
    │   │   ├── controllers
    │   │   │   └── SampleController.scala
    │   │   └── models
    │   │       └── HogeModel.scala
    │   └── conf
    └── webtest
        ├── app
        │   └── models
        │       └── MockHogeModel.scala
        └── test
            ├── controllers
            │   └── SampleControllerSpec.scala
            └── models
                └── HogeModelSpec.scala
```

webtest.dependson(web) 的な

デメリットは言わずもがな、最初の構築がめんどい(なれればそうでもないんですけど…)

あとフツーにMockitoとか使えばいいじゃんとかあると思いますが、
その辺はきっと好み？

個人的には上記構造だと webtest モジュールにMock的なものだったりFixture的なものだったりするものはゴリゴリ切り出せるので、ガリガリ切り出しています

PlayでUnitTest とは関係なくなってきている気がする…

### FakeApplicationとかはいつ使っているのか？

あくまで個人的にはですが、基本的にはrouteまで確認するような結合テスト的な、シナリオテスト的なところで使えばいいんじゃないかな…みたいな。

まあ面倒なので出来る限り使わないようにしています(おい)

## まとめ

そもそもフツーのsbtのプロジェクトとして切り出し、playはroutingとcontrollerだけ使うようにするのがいい気がする

という状況が本当は一番テストしやすいと思いますｗ

明日は… まだ誰もいない？

気が向いたらこの辺でやっていることを別エントリーで書きます（たぶん