LoginSignup
49
48

More than 5 years have passed since last update.

poltergeistとfeature specで簡単なパフォーマンステストを実現する

Last updated at Posted at 2013-10-24

最近、JSも交えた総合テストを実施するのにRails界隈で良く使われている(と思う)jonleighton/poltergeist
poltergeistはWebブラウザ操作DSLであるcapybaraのドライバとして動作します。
実際はphantomJSがバックエンドとして動いています。
phantomJSとRubyがどうやってやり取りしてるかは気が向いたら書くとして、
今回はpoltergeistを使って行う簡単なパフォーマンステストについて書きます。

ちなみに複数のクライアントから同時にアクセスするタイプのものではなく、
一つのクライアントでアクセスした時のレスポンスが来るまでの時間を測る感じです。
ラッシュかけたい場合には使えないので他の方法を考えましょう。

RSpecとcapybaraでfeature spec、つまり総合的なシナリオテストを書く時は以下のようになります。

feature "ユーザーを検索して一覧できる", js: true do
  scenario "ユーザー検索ページを開き、ユーザーを検索して一覧する" do
    visit users_path
    find("#search_user_field").set("username")
    click_button('Search')

    expect(page).to have_selector(".user")
  end
end

ページ開いてフォームに値入れて検索ボタンを押して結果確認、って感じです。
実際は、こんな簡単では無いですが…。

で、この検索結果のレスポンスが平均200ms以下で帰ってきて欲しいとします。

ここで、rack/rack-contribを利用します。
rack-contribにはRack::Runtimeというミドルウェアが含まれてます。
これはRack::Runtime以下の処理に入って戻ってくるまでの処理時間をX-Runtimeというキーでレスポンスヘッダに追加してくれるミドルウェアです。

これを利用することで、実際にコントローラーがリクエストを受け取って、レンダリングして処理を返すまでの時間が簡単に測れます。
後はこれをrspec側で取得して集計できればオッケーです。

レスポンスヘッダの情報を取得する時にpoltergeistで使える機能は二つあります。
それがresponse_headersnetwork_trafficです。

response_headersは、最後にvisitで移動した際のレスポンスヘッダをハッシュの配列として返してくれます。
単純なハッシュじゃないので微妙にアクセスしづらいですが。

network_trafficは、今までに発生したアクセス全てのリクエストとそれに対応する結果の記録を返してくれます。
これはブラウザ内のJSの動作で発生したAjaxのリクエストや画像の取得等も含みます。
これは地味に結構便利な機能です。

この例だとresponse_headersで取得するだけで充分ですが、もしAjaxでフォームの送信が行われるような場合はnetwork_trafficを使うと良いでしょう。
ちなみにnetwork_trafficclear_network_trafficで消去できます。

これでヘッダの情報が取れるのでX-Runtimeの情報を集めて集計しましょう。

feature "ユーザーを検索して一覧できる", js: true do
  scenario "ユーザーを検索して結果が返るまでの平均時間が200ms以下であること" do
    response_times = collect_response_time(20, "POST", %r(/users/search$)) do
      visit users_path
      find("#search_user_field").set("username")

      click_button('Search')
    end

    expect(response_times.sum / 20).to be <= 0.2 # Enumerable#sumはActiveSupport
  end
end

def collect_response_time(n, http_method, url_pattern, &block)
  response_times = []

  n.times do
    page.driver.clear_network_traffic # 前ループの履歴を消す

    block.call

    response_times << page.driver.network_traffic.find {|t|
      t.method == http_method && t.url =~ url_pattern # アクセス対象を特定する
    }.response_parts.first.headers.find {|h|
      h["name"] == "X-Runtime"
    }["value"],to_f # nameがX-Runtimeのヘッダのvalueを取得する
  end

  response_times
end

並列化とか考えてないのでそれなりに時間かかりますが、メタデータで上手く分けておいてJenkinsに日次で流すなどしてもらうのが良いかと思います。

そこまで正確ではないし1リクエストごとのレスポンスですが、メイン機能や重い計算が走る処理などに対して、手慣れたRSpecでざっくりとレスポンスが計測出来ると結構便利です。

49
48
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
49
48