体育の日って高速に唱えるとテストの日に聴こえる

最近書いているWebアプリは、HTTPリクエストを送ってレスポンスと状態をテストする、というテストだけ書くようにしてる。リクエストするとブログエントリを返す、というサービスだとこういう風なテストを書いてる。(HTMLを返すようにすると話が広がって説明が面倒なのでJSONを返すAPIで説明する)

describe "Entry resource" do
  let(:params) do
    {}
  end

  let(:env) do
    { "HTTP_AUTHORIZATION" => "Bearer: #{access_token.token}" }
  end

  let(:access_token) do
    AccessToken.make(user: user)
  end

  let(:user) do
    User.make
  end

  let(:entry) do
    Entry.make(author: author)
  end

  let(:author) do
    User.make
  end

  describe "GET /entries/:id" do
    context "with deleted entry's id" do
      before do
        entry.delete
      end

      it "returns 404" do
        get "/entries/#{entry.id}", params, env
        response.status.should == 404
      end
    end

    context "with other's draft entry's id" do
      before do
        entry.update_attributes(published_at: nil)
      end

      it "returns 404" do
        get "/entries/#{entry.id}", params, env
        response.status.should == 404
      end
    end

    context "with own draft entry's id" do
      before do
        entry.update_attributes(published_at: nil)
      end

      let(:author) do
        user
      end

      it "returns 200" do
        get "/entries/#{entry.id}", params, env
        response.status.should == 200
      end
    end

    context "with published entry's id", :autodoc do
      it "returns the entry" do
        get "/entries/#{entry.id}", params, env
        response.status.should == 200
        response.headers["Content-Type"].should == "application/json; charset=utf-8"
        response.body.should be_json(
          "id" => entry.id,
          "title" => entry.title,
          "body" => entry.body,
        )
      end
    end
  end
end

言語化したことなかったけどこういう慣習があると思う。

  • 1テストケースで1リクエスト
  • 内部でRouter/Dispatcher、Controller、Modelがどうなっているかというのはテストしていない
  • テストケースのネストの深さを均一にする
  • 似たテストケースが増えてきても似たようなコードを真面目に書く
  • テストのメッセージに規則を持たせる
  • 例: 1階層目のdescribeに与える文字列は"リソース名 resource"という形式
  • 例: 2階層目のdescribeに与える文字列は"METHOD Path"という形式
  • 例: contextに与える文字列は"with ..."で始める
  • 例: itに与える文字列は三人称単数系で始める
  • 正常系にタグを付ける

これまで色々な規則性を持たせてテストを書くことを試してきたけど、他のは進化の過程で淘汰されて、最終的にこの慣習が最も長く生き残った。ドキュメント形式でのテストの出力結果を見ると大体仕様が分かって便利、という側面もあるけど、このテストを実行すると謎の技術APIドキュメントがMarkdownで生成されるようになっているというのが良いところだと思う。全てこの規則の上に成り立っている。正常系用のタグが付いているテストケースごとに、次の情報が記載される。

  • Method
  • URL
  • 渡せる入力値
  • 何が起こるか
  • サンプルリクエスト
  • サンプルレスポンス

https://github.com/r7kamura/autodoc/blob/master/spec/dummy/doc/recipes.md

開発者用にアプリ側にAPI documentationみたいページを用意してあって、アクセスするとこのMarkdownファイルをいい感じに表示するようにしてる。テストを書くとドキュメントが生成されるので、テストを書くことに対する見返りが大きい。ControllerやDispatcherの内部実装は全くテストしていないけど、C0 coverageは99.0%ぐらいあった気がする。今のところこの方法で困っていることが特に無いのだけど、敢えて言うなら仕様バグみたいなものが見つからないので、関心の埒外の出来事が起きて爆発する可能性がある。だから、テストの出力結果を睨みながら、この仕様はおかしい、ということに人間が思い至る必要がある。

落ちはないけど、JSON API用にドキュメントを自動生成する実装があるので良かったら参考にどうぞ。
https://github.com/r7kamura/autodoc