Scala.jsのテストでPromise，Futureを使う (ScalaTest)

はじめての投稿となります．  
Scala.jsに関するTipsが（あるかもしれないけど）Qiita上で見つかりづらかったのでいくつかまとめました．分けるほどの内容も無いけど，検索時の利便性を鑑み分割で投稿します(3から4つ)．

まずは[Scala.js](http://www.scala-js.org/)の試用中に，並列処理周りでハマったのでメモがてら書き残し

# まとめ
* Scala.jsの単体テストでScalaTestを利用する方法
* Scala.jsの単体テストで，`Promise`，`Future`を使う方法

# テスト環境

|ソフトウェア|バージョン|
|:---|:---|
|Scala|2.11.7|
|Scala.js|0.6.6|

# 基本事項
## 基本1: Scala.jsでのテスト
[公式のチュートリアル](www.scala-js.org/tutorial/basic/)では，utestを使う事が推奨(?)されている．utestを利用する場合は，チュートリアルの通りになので省略する．ただ，

1. utestを自分はよく知らない
2. xUnitフレームワークっぽい記法でテスト名を書きづらい

等の理由から，使い慣れたScalaTestを使っている．なぜか何処にもドキュメントが無いが，[公式のリリースノート](http://www.scalatest.org/release_notes/3.0.0)を見る限り，3.0.0-M15からScala.jsが完全にサポートされたようだ．build設定も難しくなく，`build.sbt`に

```scala
libraryDependencies += "org.scalatest" %%% "scalatest" % "3.0.0-M15" % "test"
```

と書くだけ．Scala.jsのバージョンに合わせるため，`%%%`を使うこと，指定するバージョンにさえ気をつければ普通のScalaTestの設定方法とほぼ変わらない．
ちなみに，バージョンは`3.0.0-M10`でも今の所問題なく動いているので問題が起きればダウングレードも検討すべきかもしれない．

## 基本2: Scala.jsでの並列処理
JavaScriptといえば非同期API．当然，Scala.jsでも非同期APIを使うことはよくあり，`scala.concurrent.{Promise|Future}`を使う．使い方は[ここ](http://blog.pab-tech.net/2014/03/07/scala-js-future.html)などにある通りで，

```scala
import org.scalajs.dom
import org.scalajs.dom.window

val promise = Promise[Int]
window.onload = (e: dom.Event) => {
  promise.success(10)
}

promise.future foreach (result => println(result))
```

と書くと，ページ読み込みの終了後に"10"がコンソールへ出力される．このあたりは，普通のScalaと変わらない．

# 並列処理に関するテスト
非同期処理を使ったら，当然それのテストが書きたくなる．例えば，jQueryを利用して適当なWebページを持ってきて，その内容に対するテストを書くなら，

```scala
import org.scalatest._

import org.scalajs.dom
import org.scalajs.dom.window
import org.scalajs.dom.document
import org.scalajs.jquery.jQuery

import scala.concurrent.Promise
import scala.scalajs.concurrent.JSExecutionContext.Implicits.runNow

class ScalaTest extends WordSpec with Matchers {
  "jQuery" can {
    "get the content of HTML by using get function." in {
      val promise = Promise[dom.Event]

      jQuery.get(window.location.href, null, (e: dom.Event) => {
        promise.success(e)
      })

      for { event <- promise.future} {
        event should not be (null)
      }
    }
  }
}
```

みたいに書けそうに思える．しかし，このコードは**assertionが呼ばれたり呼ばれなかったり動作が不安定**になる．これは，

1. ScalaTestのテストケースが呼ばれる．
2. `jQuery.get`で，HTTPリクエストを飛ばす．
3. 非同期APIのため，ScalaTestは次の処理に進み，終了する．
4. (HTTPレスポンスが返ってくる． or `sbt test`が終了する)

となってしまい，assertionに処理が入らない．これだとテストにならないので，どうにかしないといけない．

## 解決方法
ScalaTest 3.0.0で，Async testing stylesが追加された．これを使うと，上記問題を解決できた．

```scala

import org.scalatest._

import org.scalajs.dom
import org.scalajs.dom.window
import org.scalajs.dom.document
import org.scalajs.jquery.jQuery

import scala.concurrent.Promise
import scala.scalajs.concurrent.JSExecutionContext.Implicits.runNow

class AsyncScalaTest extends AsyncWordSpec with Matchers {
  "jQuery" can {
    "get the content of HTML by using get function." in {{
      val promise = Promise[dom.Event]

      jQuery.get(window.location.href, null, (e: dom.Event) => {
        promise.success(e)
      })

      for { event <- promise.future} yield {
        event should not be (null)
      }
    }
  }
}
```

`AsyncXXX`(ここでは`AsyncWordSpec`)は，`Future[Assertion]`をかえす必要がある．そのため，最後に，`for yield`文を使ってその中にassertionを書き，`Future`を作ってあげるとコンパイルが通り，想定通りにテストが動く．
ちなみに，公式ドキュメントで書かれているutestには，このような非同期処理のテストを行う方法は書いていない．別にScala.jsに特化したテスティングフレームワークではないみたいので，わざわざ学習する必要はないのでは無いのではと感じた．