ScalaのシンプルなテスティングライブラリMinitestを使ってみる

Twitterで以下の記事を見てMonixの開発用に作られたというMinitestというテスティングライブラリの存在を知りました。

alexn.org

GitHubリポジトリはこちらです。

github.com

ScalaのテスティングライブラリというとScalaTestとSpecs2が双璧ですが、Minitestはこれらと比べると非常にシンプルなライブラリで、ScalaTestのFunSuiteからの移行がしやすいようデザインされているようです。使い方は上記の記事とGitHubのREADMEを見れば大体わかると思います。

試しに簡単なテストをMinitestで書いてみたのですが、ScalaTestのFunSuiteからであれば概ね以下のような作業で移行できそうです(FunSuiteとの互換性重視ならassertThrowsを敢えて変えなくてもよいのではという気もしますがなんで変えているのかは謎)。

  • テストケースをclassからobjectに変更し、親クラスをFunSuiteからSimpleTestSuiteに変更する
  • ScalaTestが提供する===などの比較演算子を使っている場合、通常の比較演算子に変更する
  • assertThrowsinterceptなど、一部の非互換なAPIを書き換える

assert()の第二引数にはエラー時のメッセージを指定できますが、値の比較であればassert()ではなくassertEquals()を使うと失敗したときに以下のようなメッセージを出力してくれるようなのでこちらを使ったほうがよさそうです。

- CRUD operation *** FAILED ***
  received UsersRow(1,takezoe,None) != expected UsersRow(1,takezoen,None) (SlickBlockingAPISpec.scala:33)
    minitest.api.Asserts.assertEquals(Asserts.scala:68)
    minitest.api.Asserts.assertEquals$(Asserts.scala:62)
    com.github.takezoe.slick.blocking.SlickBlockingAPISpec$.assertEquals(SlickBlockingAPISpec.scala:9)
    com.github.takezoe.slick.blocking.SlickBlockingAPISpec$.$anonfun$new$2(SlickBlockingAPISpec.scala:33)
    ...

APIはシンプルですが、Futureのアサートや、オプションでScalaCheckとの連携など、必要な機能は一通り揃っている感じです。また、Scala.jsでも使用することができます。

実際自分もScalaでテストを書くときはScalaTestでFunSuiteを使ってassert()しかしないので、Minitestのデザインはピッタリな感じがします。Specs2などはScalaの記述能力を駆使して様々な変態的な記述が可能ですが、正直機能が多すぎて覚えきれないのでこのくらいでいいんじゃないかと思いますが、assertEquals()での比較に失敗した場合のエラーメッセージなどについてはもうちょっと頑張って欲しい感があります。

IntelliJ IDEAハンズオン――基本操作からプロジェクト管理までマスター

技術評論社さんから今井さんと山本ユースケさんのIntelliJ本をお送りいただきました。ありがとうございます!ScalaIOのためしばらく留守にしていたので受け取りが遅くなってしまったのですが、この週末にざっと目を通してみました。

ページ数は200ページちょっとでスクリーンショットが多くなりがちなIDEの本としてはさほど多くないのですが、IntelliJは機能の豊富さの割に覚えることが少なくて、基本的な部分だけ押さえておけばいろんな機能を使えるという感じがあります。この本の構成や分量にもそれが現れているのではないかと思います。

全体的には入門者向けの本ではあると思うのですが、普段IntelliJを使っている自分でも知らない機能や設定、ショートカットがちょこちょこあったりして眺めているだけでも結構楽しいです。データベース機能やChrononを使用したデバッグなど、Ultimateの販促コンテンツ(?)もあります。

特に序盤の補完機能については自分もあまり使いこなせていないなぁと思いましたが、IntelliJではCTRL+SPACE(コード補完)とSHIFT×2(なんでも検索)、Command+F12(クイックアウトライン)、ALT+ENTER(クイックフィックス)くらいを覚えておけばそれなりに便利なので、細かい便利機能をなかなか覚えられないんですよねw この本でIntelliJをさらに使いこなしていきたいと思います。

Scalatra 2.6.0をリリースしました

Scala用のSinatraライクなWebフレームワークScalatraの最新バージョン2.6.0をリリースしました。

github.com

今回は新しいバリデーションフレームワークとしてscalatra-formsが追加されました。これは元々GitBucketで使用していたものをScalatra本体の1モジュールとして取り込んだものです。PlayのFormのような感じでリクエストパラメータのバリデーションとScalaオブジェクトへのマッピングを行うことができます。

また、今後のメンテナンスコスト削減のために2.5.x以前のバージョンで非推奨になっていた機能を削除するとともに、あまり使われていない機能やメンテナンスが難しい機能などを非推奨にしています。これらの機能は2.7.0で廃止される予定です。

詳細な変更内容は以下のアナウンスを参照していただければと思います。 http://scalatra.org/2017/11/11/2017-11-11-scalatra-2-6-0-released.html

今回は@magnolia-kさんにコミッタになっていただいてから初めてのリリースだったのですが、ドキュメントやサンプル、sbtプラグインの修正など、自分が見落としていた部分をかなりカバーしていただきました。また、@xuwei-kさんにも多くのプルリクエストをいただきました。ありがとうございました。

次の2.7.0では非推奨機能の廃止に加えてSwagger 3.0のサポートなどを予定しています。

次期Scalatraのバックエンドとしてのhttp4s

Scalatraの次期バックエンドをhttp4sにしようという計画は数年前からありつつ中々進んでいなかったのですが(ブランチに途中まで試みたと思われる残骸があるのですが、そもそもScalatraの開発自体が停滞しており絶賛放置されていました)、そろそろやってみようかと思いhttp4sを触り始めています。

github.com

http4sとは?

http4sはいわゆるノンブロッキングなHTTPツールキットで、今だとAkka HTTPが競合になるかと思います。http4sはAkka HTTP同様ルーティング用のDSLも備えていて単体でシンプルなWebフレームワークとして使用することもできます。

http4sの特徴の1つとして、http4s用に開発したアプリケーション(http4sではサービスと呼びます)を、サーブレットとして動かすためのアダプタが用意されているという点があります。現在ScalatraはサーブレットベースのWebフレームワークであることで、

といったメリットがあります。これらは既存のJavaベースのシステムに部分的にScalaを導入して行く際に便利なのですが、バックエンドをhttp4sに移行することでサーブレットを使うか、ハイパフォーマンスなblaze(fs2)を使うかを選択することができるようになり、Scalatraの活用の幅がより広がるのではないかと思います。

http4sを動かしてみる

http4sのサービスはこんな感じで実装します(最新のマイルストーンビルドである0.18.0-M5を使用しています)。

package com.example.http4squickstart

import cats.effect.IO
import io.circe._
import org.http4s._
import org.http4s.circe._
import org.http4s.dsl.Http4sDsl

object HelloWorldService extends Http4sDsl[IO] {
  val service = HttpService[IO] {
    case GET -> Root / "hello" / name =>
    Ok(Json.obj("message" -> Json.fromString(s"Hello, ${name}")))
  }
}

blazeで動かす場合はこんな感じ。

package com.example.http4squickstart

import cats.effect.IO
import org.http4s.dsl.Http4sDsl
import org.http4s.server.blaze.BlazeBuilder
import org.http4s.util.StreamApp

object BlazeServer extends StreamApp[IO] with Http4sDsl[IO] {
  def stream(args: List[String], requestShutdown: IO[Unit]) =
    BlazeBuilder[IO]
      .bindHttp(8080, "0.0.0.0")
      .mountService(HelloWorldService.service, "/")
      .serve
}

サーブレットで動かす場合はこんな感じのリスナーを作っておけばOKです。これをxsbt-web-pluginで実行したり、warにしてサーブレットコンテナにデプロイする感じかと思います。

package com.example.http4squickstart

import javax.servlet.annotation.WebListener
import javax.servlet.{ServletContextEvent, ServletContextListener}
import org.http4s.servlet.syntax.ServletContextSyntax

@WebListener
class Bootstrap extends ServletContextListener with ServletContextSyntax {
  override def contextInitialized(sce: ServletContextEvent) = {
    val context = sce.getServletContext
    context.mountService("helloService", HelloWorldService.service)
  }
  override def contextDestroyed(sce: ServletContextEvent) = ()
}

まだ細かいところまで見れていませんが、同じサービスがバックエンドを変えても動作することが確認できました。

http4sは国内ではだいぶマイナー感がありますが、Typelevelプロジェクトの1つ(まだIncubatorですが)ということもあり、海外ではそこそこ使われている気配があります。今のところ開発も活発ですが、Scalatraのバックエンドに採用したものの、肝心のhttp4sの開発が止まってしまうと厳しいので、そのあたりの様子も見つつ本当に移行できるかどうかを検討していきたいと思います。

ScalaIO 2017に参加してきました

f:id:takezoe:20171107031414j:plain

11月2日、3日にフランスのリヨンで開催されたScalaIO 2017にスピーカーとして参加してきました。このエントリでは参加したセッションの中から印象に残ったものを紹介したいと思います。

Compiling like a boss!

Scalaで分散コンパイルによるコンパイル速度の高速化を実現するHydraのセッション。事前のスケジュールではTriplequoteのお二方でのトークとなっていましたが、Iulianさんは不在でMircoさん単独でのトークでした。

ScalaDaysでのセッションとほとんど同じ内容だったと思いますが、今回のプレゼンテーションではIntelliJでのサポートが利用可能になる、Hydraのモニタリングが可能になるという2点のアップデートがありました。以下のスライドは今年のScalaDaysで使用したもののようですが、上記二点のアップデート以外は今回も同じ内容のものだったと思います。

Move fast and fix things

Scala Italyの主催者でもあるbuildoのGabriele PetronellaさんによるScalafixのセッション。

Scalafixのコンセプトや、内部でどのようなことをしているのかがわかりやすく説明されていました。ScalametaがASTだけでなくソースコードの情報も持っているので、単にプログラムをASTで操作するだけでなく、コメントを残して部分的にパッチをあてたりできるというところが面白かったです。

The Making of an IO

SlamDataのDaniel SpiewakさんによるIOモナドの話…だったのですが、Scalaz7のTaskを壮絶にディスった後cats-effectの紹介をするという流れでした。スレッドプールをどう分けるべきか、どのくらいのスレッドを割り当てるべきかの指針も示されていました。

Danielさんが異常にハイテンションかつオーバーアクションでステージを常に動き回っているのが印象的でした。早口だったので聞き取れない部分も多かったのですが、とにかく勢いがあり、時間の経過をわすれさせる熱量がありました。

The Design of the Scalaz 8 Effect System

こちらはSlamDataのCTO、John A. De Goesさんのセッション、両者ともSlamData所属、catsとScalazのIOということで完全に狙った配置だと思いますが、Danielさんによるcatsのセッションの直後に同じ会場でScalaz8 Effect Systemの話でした。

Scalaz8のEffect Systemはスレッドではなくfiberを生成するfork/joinフレームワークに基づいており、リソースのリークもなく性能も非常に優れているとのことでした。

Johnさんは11月11日〜13日にサンフランシスコで開催されるScale By the Bay 2017でも同じ話をされるようです。また、ScalaMatsuri 2018にもトークを応募されているようです。タイトルは違いますがIOの話のようなので同じ話が日本でも聞けるかもしれません。

Case classes ate my RAM

FindifyのRoman Grebennikovさんによるセッション(Findifyはスウェーデンの企業のようですが、Romanさんはロシア在住のようです)。Scalaのコレクション(に格納されるケースクラス)のメモリのフットプリントを気合いで圧縮するという話でした。

Shapelessでケースクラスをほげほげしてプロパティの値を直接メモリ(バイト列?)にマッピング/復元するという荒技を使っているようです。まだプロダクションでは使っていないとのことでしたが、圧縮・展開にかかるオーバーヘッドとメモリ削減のトレードオフなどベンチマーク結果に対する考察もあり、面白いセッションでした。

github.com

こちらも11月23日〜25日にスロベニアで開催されるBeeScala 2017で同じ話をされるようです。

Next generation Scala builds with CBT

Slickなどでお馴染みJan Christopher Vogtさんのセッションで、Scala Days New Yorkでも聞いたcbtのセッションです。

ScalaDaysで聞いたときは正直完全にネタだと思っていたのですが、かなり本気で取り組んでいるようで、ビルドツールとして必要な機能は一通り実装されたようです。cbtのコンセプトを説明した後はデモ中心で各機能の紹介をしていました。IntelliJサポートもIntelliJの次のバージョンから利用可能になるとのことです。

ちなみにこのIntelliJサポートはロシアの学生さんがGoogle Summer of Codeで開発したものだそうで、本人が壇上でデモをしていたのですが、後で話したらめっちゃ緊張してたそうです。全然そんな感じには見えなかったし、英語も苦手と言ってたのに上手ですごいなぁと思いました。

github.com

Q&Aでの突っ込みがかなり激しかったのですが、個人的にビルドツールに自分の名前を付けてしまうお茶目さに惹かれているのでワンチャン目指してこれからも頑張って欲しいです。

Open Source Spreeについて

自分はずっとトークを聞いていたので参加しなかったのですが、会場ではトークセッションの他にハンズオンやScala Center主催のOpen Source Spreeなどが開催されていました。Open Source SpreeというのはScala関連のOSSにその場でコントリビュートしてみよう(コントリビュートするとTシャツがもらえる)というもので、Scala Centerが各地のScalaカンファレンスで開催しているようです。

github.com

https://camo.githubusercontent.com/ff0133f2a3c26674002a16eb36e19d66cdd8e1d4/68747470733a2f2f7062732e7477696d672e636f6d2f6d656469612f43746e43727476574141414f306e452e6a70673a736d616c6c

今回のScalaIOではトークも採択されたScalaz、scalafix、cbt、hamstersなどのコミッターがチューターとして協力していたようです。自分もPredictionIOやGitBucket、Scalatraなどで協力できたらとは思ったものの、さすがに英語力的に厳しいと思ったので今回は手を挙げませんでしたが、将来的にはこういうこともできるといいなと思います。

全体的な感想など

ここ1〜2年ほど、Scalaのカンファレンスではどこもかしこもフリーモナドの話ばかりだったような気がするのですが、今回はFreestyleのトークが1つあったくらいでフリーモナドブーム(?)は一段落したのかなという印象でしたw ScalaDays後で大きなトピックがなかったせいか、逆にバラエティ豊かなトークが揃っていたのではないかと思います。ショートセッションやLTも多様性という意味でよく機能していたと思います。

今回初めてScalaDays以外の海外カンファレンスに参加したのですが、ScalaMatsuriなどと同じく現地コミュニティの方々の尽力で運営されている様子がよくわかりました。また、それと同時にScalaMatsuriの運営の素晴らしさも感じました。海外スピーカーの豪華さはScalaMatsuriも引けを取っていないと思うのですが、やはりスピーカーが作者だったりコミッタだったりするケースが多いのでそういう部分での質の高さや、Q&Aのやり取りなどにレベルの高さを感じました。

全体的にはScalaDaysとは一味違ったアットホームな雰囲気があり、リラックスして参加できるカンファレンスだったと思います。フランス語のセッションと英語のセッションがあり、自分は英語のセッションに参加していたのですが(フランス語はまるでわからないので…)、フランス語のセッションの中でもscala-hamstersの作者の方の話や、Alpakkaにコントリビュートしてみた話などは興味があったので可能であれば参加してみたかったです。

Akka HTTPでWebJarsとTwirlを使う

仕事でちょっとしたツール(Webアプリ)を作るのに試しにAkka HTTPを使ってみています。Akka HTTPでJSONを使う方法については以前このブログでも紹介しましたが、

takezoe.hatenablog.com

今回はいわゆるHTMLを返すシンプルな作りの管理ツールなのでJavaScriptCSSライブラリはWebJarsで、HTMLのレンダリングはTwirlを使うようにしてみました。

WebJars

ThoughtWorksリポジトリにAkka HTTPでWebJarsを使うためのライブラリがありました。

github.com

メンテが止まっているようですが、リポジトリ内のWebJarsSupportは現在のAkka HTTPでもそのまま利用できます。実装としては非常にシンプルで、webjars-locatorで取得したクラスパス内のリソースをgetFromResource()で返しているだけです。

これを使う場合はbuild.sbtに以下の依存関係を追加する必要があります。

"org.webjars" % "webjars-locator" % "0.32"

WebJarsのリソースを返すエンドポイントはこんな感じ。/webjars/jquery.jsのようなパスでWebJarsでインポートしたJavaScriptCSSを参照できます。

import com.thoughtworks.akka.http.WebJarsSupport._

get {
  pathPrefix("webjars") {
    webJars
  }
}

Twirl

Twirlもライブラリを作っている人がいました。

github.com

こちらも最新のAkka HTTPのバージョンには追従できていないようですが、TwirlのHtml型などに対応するマーシャラーを定義しているだけのシンプルなものなので、リポジトリ内のTwirlSupportをそのまま持ってきて使ってもよいでしょう。

これを使う場合は普通にTwirlを使う場合と同様の設定が必要になります。まずはproject/plugins.sbtにTwirlプラグインを追加。

addSbtPlugin("com.typesafe.sbt" % "sbt-twirl" % "1.3.4")

build.sbtでTwirlプラグインを有効化します。

enablePlugins(SbtTwirl)

Twirlのテンプレートファイルはsrc/main/twirlディレクトリ配下に作成し、エンドポイントからは以下のようにして呼び出します。

import akkahttptwirl.TwirlSupport._

get {
  path("hello") {
    // src/main/twirl/hello.scala.html
    complete(html.hello())
  } ~
}

おまけ:静的ファイルを返す

おまけとしてAkka HTTPで静的ファイルを返す方法を書いておきます。ファイルシステム上のファイルを返す場合はこんな感じ。

get {
  pathPrefix("assets") {
    getFromDirectory("assets")
  }
}

クラスパス内のリソースを返すこともできます。

get {
  pathPrefix("assets") {
    getFromResourceDirectory("assets")
  }
}

とりあえずこのくらいでAkka HTTPで普通のWebアプリを作ることができるんじゃないかと思います。フォームのバリデーションなどもあると便利ですが、そのあたりも拡張して便利に使えるものを組み込んでしまうのがよさそうです。

オープンソースの機械学習プラットフォームまとめ

PredictionIOは機械学習を使用したアプリケーションを開発・運用するためのプラットフォームを提供するためのOSSですが、世の中には他にも同じ領域のOSSが存在します。PredictionIO含めて各プロダクトの特徴をまとめてみました(PredictionIO以外はドキュメントやソースをチラ見して書いているので見落としていることなどあるかもしれませんがご容赦いただければと思います)。

PredictionIO

github.com

Apache Software Foundationで開発されている機械学習プラットフォームです。基本的にSpark上で動作する機械学習ライブラリをターゲットにしていますが、最近はPython対応なども行われています。作成したマイクロサービスはSprayベースのAPIサーバとして起動することが可能です。用途に応じたテンプレートが多数用意されており、それをカスタマイズして使用できるのが特徴です。

Apacheのプロジェクトですし、ドキュメントやサンプルもそれなりに揃っています。日本人コミッタもおり、国内でのイベントも開催しているので、他のプロダクトと比べると情報を得やすいのもメリットといえます。

PredictionIOの概要については以下のQiitaの記事が参考になります。日本語のハウツー記事も多いので、ググってみていただけるとよいかと思います。

qiita.com

Seldon

github.com

基本的にはPredictionIO同様のユースケースが想定されており、Predict、Recommend用のサービスを構築可能。Spark、scikit-learn、TensorFlow、Kerasなどの機械学習ライブラリをサポートしていたり、作成したマイクロサービスをDockerコンテナとしてデプロイ可能、Graphanaによるマイクロサービスのモニタリング機能も自前で備えているなど機能的にはPredictionIOよりも先行している部分がありますが、ドキュメントが豊富とはいえず、日本語はもちろんのこと英語での情報もまだまだ少ないようです。ただ、企業がやっているOSSなので、そこはコンサルしますよということなのかもしれません。

日本語では以下のQiitaの記事が参考になると思います。

qiita.com

MLeap

github.com

MLパイプラインをシリアライズ/デシリアライズし、実行するためのライブラリを提供します。Spark、scikit-learn、TensorFlowをサポート。生成した予測モデルを使用してマイクロサービスとしてデプロイするためのDockerイメージも用意されています。PredictionIOやSeldonと違って機械学習を運用するためのプラットフォームというよりはパイプラインや予測モデルのポータビリティを実現するためのライブラリという感じです。

ドキュメントはSeldonと比べるとかなりしっかりしていますが、日本語情報は今のところ皆無です。

まとめ

アプリケーションに機械学習を活用した機能を組み込むことが一般的になるにつれ、それらを効率よく開発・運用するためにこのような機械学習プラットフォームの必要性は高まってくると考えています。SeldonやMLeapのように複数の機械学習ライブラリを扱うことができる仕組みも必要になってくると思いますし、逆に機械学習の専門家がいないチームではPredictionIOのように予め用意されているテンプレートを使用することで機械学習を利用できるというメリットもあるでしょう。

どのプロダクトも作成した予測モデルをマイクロサービスとしてデプロイしてアプリケーションから利用するという形は共通していますが、一方でバッチ的に使用したい場合やオンライン学習などのユースケースをどう取り込んでいくのかという点についても今後の注目ポイントかもしれないと思います。

また、今回はOSS機械学習プラットフォームを紹介しましたが、AWSやAzure、GCPなどで提供されているマネージドな機械学習プラットフォームももちろん有力なオプションです。機械学習プラットフォームの動向という意味ではこれらについても注視していく必要がありそうです。