岩本隆史の日記帳(アーカイブ)

はてなダイアリーのサービス終了をうけて移行したものです。更新はしません。

Pinto公開に向けて #19 ― HTTPレスポンスヘッダのテストを考える

あらすじ

Pintoというソーシャルブックマークサービス、およびPintoBeansというWebアプリケーションフレームワークRubyで開発中です。その際に気づいたことや工夫した点などを、備忘録を兼ねて書いています。その19回目です。

Rackの挙動を確認

PintoではRackというライブラリを使います。SPECには下記のようにあります。

A Rack application is an Ruby object (not a class) that responds to call. It takes exactly one argument, the environment and returns an Array of exactly three values: The status, the headers, and the body.

よって、Rackベースのアプリケーションを単純化すると、こうなります。

class PintoApplication
  def call(environment)
    [
      200,                               # status
      {'Content-Type' => 'text/plain'},  # headers
      'under construction'               # body
    ]
  end
end

Pintoのトップページ仕様

昨日の日記に書いたとおり、PintoはTDD(BDD)で開発しようと思っています。何はともあれ、トップページのテスト(仕様)を書かなければすっきりしません。

ざっくり、トップページの仕様は下記のような感じです。

  • URLは http://pinto.jp/
  • GET、HEAD、OPTIONSメソッドのみ許可
  • ステータスコードは300(Multiple Choices)
  • キャッシュ可能(6時間ぐらいにしとく?)
  • XHTML1.1、UTF-8
  • タイトル(title要素、h1要素)は「Pinto」
  • 英語版(http://en.pinto.jp/)と日本語版(http://ja.pinto.jp/)へのリンクを含む

テストをどう書くか

さて、下記のようなリクエストに対するテストを考えてみます。

GET / HTTP/1.1
Host: pinto.jp

Rackにはモックリクエストクラスがあるので、リクエストのエミュレートにはそれが使えます。そのリクエストに対するレスポンスが正しいかどうかのテストを書けばよいわけです。

とりあえず、ステータスコード(配列の1番目)が300であるかどうかは単純に書けますね。

レスポンスボディ(配列の3番目)も、XHTML1.1として妥当かとか、UTF-8で書かれているかとか、特定の要素が正しく出力されているかとか、そのようなテストを書けばよいでしょう。

悩むのはレスポンスヘッダ(配列の2番目)です。

妥当なレスポンスヘッダは何か

RFC 2616で規定されているレスポンスヘッダを列挙しながら考えます。

general-header

Cache-Control 6時間(21600秒)キャッシュさせたいので「max-age=21600」
Connection Webサーバが設定してくれるので出力しない
Date 同上
Pragma キャッシュありなので出力しない
Trailer Webサーバが必要に応じて設定すべきと判断して出力しない
Transfer-Encoding 同上
Upgrade 不要
Via 不要
Warning 不要

response-header

Accept-Ranges Webサーバが必要に応じて設定すべきと判断して出力しない
Age 不要
ETag Last-Modified値などを元に出力する(生成ロジックは後日考える)
Location 不要
Proxy-Authenticate 不要
Retry-After 不要
Server 不要(WEBrickは勝手に追加する)
Vary Accept系のリクエストヘッダは無視することに決めたので出力しない
WWW-Authenticate 不要

entity-header

Allow 「GET, HEAD, OPTIONS」
Content-Encoding Webサーバが必要に応じて設定すべきと判断して出力しない
Content-Language 「en」
Content-Length Webサーバが設定してくれるので出力しない
Content-Location 不要
Content-MD5 攻撃には無力らしいので出力しない
Content-Range Webサーバが必要に応じて設定すべきと判断して出力しない
Content-Type 「application/xhtml+xml; charset=UTF-8
Expires 6時間後の時刻(HTTP1.1では不要だが、そこまで考えたくない><)
Last-Modified スクリプトファイルの更新日時
extension-header 特に不要

というわけで、強調した7件について正しく出力されること、それ以外について出力されないことをテストすればOK、という結論に至りました。ああ、長かった。

ちなみに

時刻を返す部分は、テスト実行時刻に応じて値が変わるのを防ぐため、Mochaなどでスタブを作ろうと思っています。

参考
Time.nowのテスト? それMochaでできるよ - http://rubikitch.com/に移転しました

追記(2008-08-26)

ETagやLast-Modifiedを吐くと書いておきながら、304や412のケースの想定を忘れていました。

ざっくりまとめると、304の場合はETagとExpires、412の場合はエラー画面のXHTMLとともにAllowとContent-LanguageとContent-Typeを、それぞれ出力すれば良いと思っています。HTTP難しい><