Scala.jsとJVMの両対応コードとScala.jsのテストの書き方 2014年02月26日

Scala.jsの開発は最近も活発で、0.3が出てFutureなど色々機能が増えたり、Jasmineを使ってScala.jsのテストを書けるようになったり、Scala.jsとJVMの両対応のライブラリが出てきたりしました。

そこで僕もためしに、前にScalaカンファレンスJapanのハッカソンで書いた、簡単なHTML DSLのライブラリ「htmlda」のScala.js対応をやってみました。

hexx/htmlda

build.sbtからJVM用のプロジェクトとScala.js用のプロジェクトを取り出してみます。

// for JVM
val jvmSettings = baseSettings ++ Seq(
  libraryDependencies += "org.scalatest" %% "scalatest" % "2.0" % "test",
  version := htmldaVersion
)

lazy val htmlda = project.in(file(".")).settings(
  jvmSettings ++ Seq(
    name := "htmlda",
    unmanagedSourceDirectories in Compile <+= baseDirectory(_ / "shared" / "main" / "scala")
  ) : _*
)

// for Scala.js
val jsSettings = baseSettings ++ scalaJSSettings ++ Seq(
  version := htmldaJsVersion
)

lazy val htmldaJs = project.in(file("js")).settings(
  jsSettings ++ Seq(
    name := "htmlda",
    unmanagedSourceDirectories in Compile <+= baseDirectory(_ / ".." / "shared" / "main" / "scala"),
    libraryDependencies ++= Seq(
      "org.scala-lang.modules.scalajs" %% "scalajs-jasmine-test-framework" % scalaJSVersion % "test",
      "org.webjars" % "jquery" % "2.1.0-2" % "test",
      "org.webjars" % "envjs" % "1.2" % "test"
    )
  ) : _*
)

一番のポイントはScalaのソースコードをsrc/main/scala からshared/main/scalaに移して、それぞれのプロジェクトでunmanagedSourceDirectoriesとして読み込んでいるところです。 プレーンなScalaのコードならこれでそれぞれにコンパイルできるようになります。テストのコードは共通化できないので、これはそれぞれのプロジェクトで用意します。 またWebJarを使ってScala.jsのテスト用にJQueryとJavaScriptでDOMをエミュレートするEnvjsというライブラリを読み込んでいます。

続いてScala.jsのテストの書き方ですが、最近Scala.js標準ライブラリにjasmine-test-frameworkというものが入りました。 Scala.jsのコードをJavaScriptのBDDフレームワークのJasmineを使ってテストできるようにするというものです。 テストコードを見てみますと、

import scala.scalajs.js
import scala.scalajs.js.Dynamic.global
import scala.scalajs.test.JasmineTest
import com.github.hexx.htmlda.PlainHtmldaDSL._

object HtmldaJsJasmineTest extends JasmineTest {
  global.importScripts("jquery.js")

  describe("HtmldaJS") {
    it("should render HTML") {
      val jq = global.jQuery

      val node = dsl {
        %p {
          %a ($("href" -> "http://www.scala-js.org/"), t("Scala.js"))
        }
      }

      jq("body").append(jq(node.render))

      val body = global.document.getElementsByTagName("body").asInstanceOf[js.Array[js.Dynamic]].head

      val p = body.innerHTML.toString

      expect(p).toMatch(""".*

<a href=\"http://www.scala-js.org/\">Scala.js

.*""") } } }

一見普通のScalaで書かれたrspec系のBDDのテストコードに見えますが、実際にはJavaScriptにコンパイルされて、JavaScriptのJasmineを使って実行されます。 このテストではHtmldaのDSLで作られたHTMLをJQueryを使って正常にAppendできているかのテスト行っています。

以上、Scala.jsのクロスコンパイルとテストコードの書き方を見ましたが、とても簡単にできるのがわかったと思います。 まだまだコードサイズやコンパイル時間など多数の問題を抱えていますが、Scala.jsの進歩のスピードは早く、どんどん便利になってきています。 用途を限定すれば実用になる日がくるかもしれません。