Life is TraversableOnce

programming / Java / Scala / Rust

Skinny Tutorial #4 机上のクーロン(2)

関係のないおはなし

はてなブログの記事を書くときは主にiPhone版のアプリを利用しています。

統合テスト

今日も帰りの電車の中からコードを書いていこうと思います。 前回作成したStaticPagesControllerにViewを用意する前に、コントローラの統合テストを済ませてしまいましょう。

Skinny Frameworkのテストでは、テスティングフレームワークであるScalaTestを採用した書き方を推奨しているようです。 scaffoldで自動生成したテストスイートの場合、ソースファイル1件ごとに対する単体テストでは、ScalaTestのFunSpecクラスにMatcherトレイトをミックスインしたテストコードを基本としています。Matcherを使うことで、テストの合否を自然言語に近いかたちで書くことができます。

ですが今回は、Railsチュートリアルでも統合テスト(integration test)を題材としていますので、単体テストは省略して統合テストに進んでしまいましょう。

統合テストでは、アプリケーションの機能に焦点を当て、アーキテクチャの各層、クラス間の連携も考慮して、アプリケーションからの出力を検証しています。

何はともあれ、公式ドキュメントを参考に、静的なコンテンツであるHomeページが表示されることを検証するテストを書いてみましょう。

class StaticPagesIntegrationTest extends ScalatraFlatSpec with SkinnyTestSupport {
  addFilter(Controllers.staticPages, "/*")

  it "should show static home page" in {
    get("/staticpages/home") {
      status should equal(200)
      body should include("Home")
    }
  }
}

上記のコードでは、ステータスコードが200(OK)であること、レスポンスボディにHomeの文字列が含まれていることを検証しています。 RailsにおけるRspecでのテストコードにかなり近いフィーリングで記述できているように思います。

使用しているコンポーネントを見てみましょう。

スーパークラスScalatraFlatSpecクラスは、ScalaTestのテスト記述スタイルのひとつであるFlatSpecをScalatraから利用しやすくするラッパーで、Scalatraが提供しているクラスです。ScalaTestではテストをさまざまなスタイルで書くことができるように、スタイルごとのトレイトがたくさん用意されていますが、FlatSpecは振る舞い駆動開発の入門者におすすめの記法だそうです。

ミックスインしているSkinnyTestSupportはSkinnyのユーティリティトレイトで、今回はあまり関係ありませんが、テストコードからセッションスコープに情報を注入する機能を提供します。

このテストをこのまま実行しても、当然合格はできません。

TODO テストの結果を追記すること

レスポンスボディに要求された文字列を追加するべく、Viewの準備を進めていきます。

sspとレイアウト

Railsチュートリアルの第3章では、かなり後半になるまで登場しませんが、Viewテンプレートでコンポーネントを共通化し、テンプレート間で流用できるようにするレイアウトというものがあり、Skinny(Scalate)にもこのしくみが存在しています。

view/layout/フォルダ以下にあるdefault.sspが、sspテンプレートの場合のレイアウトを定義しているファイルです。 アクションで描画される内容は、このレイアウトの中に流し込まれることになります。

そのため、homeページに必要な内容はそれほど多くありません。まだコントローラから値の受け渡しなどもしないので、単純に必要な項目だけを実装しましょう。

ルーティングから期待されるファイル名は、view/staticpages/home.html.sspです。

<h3>Home</h3>
<p>
  Skinny Tutorial サンプルアプリケーション
</p>

動的なページへ

例題に沿って、このHomeページのタイトルをコントローラから変数として受け取るように改造していきます。

今回の例では、タイトルは個々のテンプレートではなく、レイアウトテンプレートで定義されています。Scalateには、個別のテンプレートの内部からレイアウトの属性を変更するメソッドも用意されていますが、わたしのアプリケーションではdefault.sspを直接変更してしまうことにします。

<%@ val body: String %>
<%@ val title: String %>
<html>
<head>
  <title>Skinny Tutorial Sample | ${title}</title>
</head>
<body>
  ...

  ${unescape(body)}

  ...
</body>
</html>

上記はScalateのサンプルをほとんどそのまま書き写したものですが、Skinnyのgenerateで生成されるレイアウトも、Bootstrapを読み込むためのscriptタグがあるだけで、同じような内容になっているはずです。

ビューを修正したので、コントローラからリクエストスコープで参照できる値を設定し、テンプレートに引き渡す必要があります。 Homeページのためのアクションに、タイトルを指定する文を追加します。

class StaticPagesController extends ApplicationController {
  def home = {
    set("title", "Home")
    render("staticpages/home")
  }
  ...
}

(つづく)