LoginSignup
2

More than 5 years have passed since last update.

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

Posted at

はじめての投稿となります.

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

まずはScala.jsの試用中に,並列処理周りでハマったのでメモがてら書き残し

まとめ

  • Scala.jsの単体テストでScalaTestを利用する方法
  • Scala.jsの単体テストで,PromiseFutureを使う方法

テスト環境

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

基本事項

基本1: Scala.jsでのテスト

公式のチュートリアルでは,utestを使う事が推奨(?)されている.utestを利用する場合は,チュートリアルの通りになので省略する.ただ,

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

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

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}を使う.使い方はここなどにある通りで,

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ページを持ってきて,その内容に対するテストを書くなら,

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が追加された.これを使うと,上記問題を解決できた.


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に特化したテスティングフレームワークではないみたいので,わざわざ学習する必要はないのでは無いのではと感じた.

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
2