Ruby 外から Web アクセスする何か (Capybara とか、cli とか) を RSpec でテストするときのアクセス先をモックする
mmmpa.mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmpa.net
のテストをこの方法で行いました。
本編
たとえば、Capybara
は各種ブラウザを介するアクセスのため、webmock
が効かず、別の gem が必要なのは有名です。 https://github.com/oesmith/puffing-billy
Capybara
でテストする際はこれでいいのですが、Capybara
をテストする場合には不便かもしれませんね。
また、バッククォートや Open3
を用いて呼びだすような cli、たとえば siege
などのターゲットをモックすることはできません。cli におんぶにだっこの gem を開発するときのテストに、かなり不便ですね。
RSpec
開始時にサーバーをたてる
望んだ結果を返すアクセス先があればいいので、テスト中に起動する仮想サーバーをたてます。
public
ディレクトリに適当な HTML を用意した上で、
RSpec.configure do |config| require 'webrick' config.before(:suite) do port = ENV['MOCK_PORT'] || 3000 host = ENV['MOCK_HOST'] || '127.0.0.1' started = false Thread.start do WEBrick::HTTPServer.new( DocumentRoot: File.expand_path('./public/', __dir__), BindAddress: host, Port: port, AccessLog: [], StartCallback: ->{ started = true } Logger: WEBrick::Log::new("/dev/null", 7) ).tap { |server| Signal.trap(:INT) { server.shutdown } server.start } end while !started sleep 0.5 end end end
Thread
はあたらしいものを用意しないと、sever.start
の時点で RSpec
のプロセスが停滞してしまいますので注意しましょう。
また、別 Thread
になるので server.start
が完了するしないにかかわらずテストに突入、テストが落ちるということがあるので、StartCallback
を使って、きちんとサーバーがスタートしたことを確認してからはじめましょう。
単純なアクセス以外
さらに、リダイレクトなど、単純な HTML アクセス以外の処理が必要ならば、インスタンス server
に mount
すると処理を追加できます。
# たとえばリダイレクト # Rails を模するなら、301(Moved) ではなく 302(Found) server.mount_proc('/redirect') do |req, res| res.set_redirect(WEBrick::HTTPStatus::Found, '/redirected.html') end
できあがり
これで、以下のようなテストが、自由に行えるようになりました。
expect(`siege -t 10s http://127.0.0.1:3000/foo.html`).to be_truthy
例は siege
自体のテストのようになってしまっていますが、実際は取得した結果をあれこれしたものをテストしました。
雑ではありますが、とても楽に、多くのアクセス先を用意できるようになりました。