継続的ブログ

主にweb系の技術について書いています

クローラー(スクレイピング)をRspecでテスト

今回初めて、個人用にクローラーを開発してみました。
JavaScript多めのサイトのため、「Capybara + Poltergeist」を使用しました。

順調に開発は進んだのですが、テストってどう書けばいいんだろうと思い、知り合いに相談したところ、下記方針に決まりました。

  • feature_specとして書く
  • 毎回アクセスするのはよろしくないので、用意したHTMLファイルを解析する

やり方

CapybaraのHTTPリクエストをstub化する

毎回アクセスしないように、CapybaraのHTTPリクエストをstub化し、用意したローカルのHTMLファイルを返却するようにします。

HTTPリクエストをスタブ化するGemとして「webmock」は有名だと思います。

github.com

しかし、これはCapybaraでは使えません。
代わりに「puffing-billy」を使います。

github.com

Gemをインストールして、設定を追加します。
README通りだと、jsのエラーが無視されずRspecが失敗してしまうため、私は以下のように設定しました。

Capybara.register_driver :pg_billy do |app|
  options = {
    js_errors: false,
    phantomjs_options: [
      '--ignore-ssl-errors=yes',
      "--proxy=#{Billy.proxy.host}:#{Billy.proxy.port}"
    ]
  }
  Capybara::Poltergeist::Driver.new(app, options)
end
Capybara.configure do |config|
  config.default_driver = :pg_billy
  config.javascript_driver = :pg_billy
end

テストを書いてみる

テストコードはこんな感じです。

require 'rails_helper'

feature 'Login' do
  background do
    Billy.config.record_stub_requests = true
  end

  given(:url) { "http://www.example.com" }
  
  scenario 'login' do
    proxy.stub(url).and_return(body: File.read("spec/features/htmls/login.html"))
    visit url

    within("#form") do
      fill_in 'id', with: 'id'
      fill_in 'password', with: 'password'
    end

    stub = proxy.stub(url, method: 'post').and_return(
      headers: { 'Access-Control-Allow-Origin' => '*' },
      code: 200,
      text: "Success"
    )
    click_button('ログイン')

    request_params = stub.requests.shift[:body].split("&")
    expect(page).to have_content("Success")
    expect(request_params).to include("id=id", "password=password")
  end
end

ポイント1

Billy.config.record_stub_requests = true

フィールドに値が入って、ちゃんとPostされているかチェックするため、リクエスト情報を記録するようにしています。

ポイント2

proxy.stub(url).and_return(body: File.read("spec/features/htmls/login.html"))

指定のURLにアクセスした際に、用意したHTMLをbodyに入れて返すようにしています。

ポイント3

stub = proxy.stub(url, method: 'post').and_return(
      headers: { 'Access-Control-Allow-Origin' => '*' },
      code: 200,
      text: "Success"
    )

ログイン処理をstub化しています。

最後に、フィールドに値がちゃんと入っていること、ログインボタンが押されたことをチェックして、テストは完了です。

なかなか情報もなく、いろいろ悩んだ末、今回このようにテストしてみました。
「うちはこうやってるよ」とかアドバイスいただけるとありがたいです!

参考

Rubyでやるならこの本はオススメです!

Rubyによるクローラー開発技法 巡回・解析機能の実装と21の運用例

Rubyによるクローラー開発技法 巡回・解析機能の実装と21の運用例