初めまして、Speee22卒エンジニア内定者の大金です。
今回、内定者インターンで開発のお手伝いをさせていただいている時に、Seleniumに関して自分が実際に詰まった内容についての記事を書きました。
Seleniumでresponseメソッドが提供されていない理由や、SeleniumとWebDriverとDriverとの関係性、Rack::Testでのresponseメソッドのお話などをまとめた投稿になります。
- この疑問を持った背景
- 周辺の用語を整理する
- Seleniumがresponseメソッドを提供しない理由
- 結局responseメソッドを使うテストをどう解決したか?
- Rack::Testがどうやってresponseメソッドを提供しているのか
- 最後に
この疑問を持った背景
RailsにてRspecの提供するsystem_specを使用しE2Eテストを書いていた際、ヘッダ情報を使ってassertionを書くためにresponseのヘッダの値が必要でした。
そこで、Capybaraが提供しているresponse_headersメソッドを使用し、ヘッダ情報を取得しようとしたのですが、以下のような「Driverがサポートされていない」というエラーが出力されました。
Capybara::NotSupportedByDriverError Capybara:Driver::Base#response_headers
また、reponseメソッドに関しても同様に利用することはできませんでした。
この時、開発環境下の設定では、system_testのdriverはseleniumのheadlessのchromeドライバである:headless_chromeを利用していました。
きっと代用responseメソッドが用意されているだろうと思い調べてみたところ、 Seleniumはレスポンスを返すメソッドを提供していませんでした....
公式Docのresponse_headersのメソッドの説明に以下のように書いてあります。
Returns a hash of response headers. Not supported by all drivers (e.g. Selenium).
でも、なぜ提供していないのか??
Seleinumでresponseの値を見ることがさほど不自然な事だと思わなかった自分は疑問に思いました。
周辺の用語を整理する
ここで自分がきちんと理解していないこともあり、WebDriver、Driver、Seleniumをそれぞれの関係性について、整理してみることにします。
WebDriver
WebDriverとはAPI群とプロトコルです。これらはウェブブラウザの動作をコントロールするための言語中立なインターフェイスを定義しています。 それぞれのブラウザは特定のWebDriverの実装を持っており、これらは driver と呼ばれます。 driverはブラウザに委譲する責務を持つコンポーネントであり、Seleniumとブラウザ間の通信を処理します。
参考: https://www.selenium.dev/ja/documentation/getting_started/
Driver
実際のブラウザを制御します。 ほとんどのドライバーはブラウザベンダー自身が作成します。ドライバーは一般的にブラウザ自体を備えたシステムで実行される実行可能モジュールであり、テストスイートを実行するシステムにはありません。(ただし、それらは同じシステムであっても構いません。)注: 一部の人々はドライバーをプロキシと呼んでいます。
参考: https://www.selenium.dev/ja/documentation/webdriver/understanding_the_components/
Selenium
Selenium(セレニウム)はブラウザのオートメーションツールです。自動でブラウザを操作することでWebサイトの動作のテストを行うことができます。 Seleniumには旧APIであるSelenium RC(Remote Control)と、新しいAPIのSelenium WebDriverがあります。
参考: https://www.codegrid.net/articles/2014-selenium-1/
これらをまとめて考えてみると、テストフレームワークにそれぞれSeleniumが提供しているWebdriverAPIを使用し、Driverを介してブラウザを操作しているのですね。
今回の話に照らし合わせて考えてみると
- RspecでCapybaraを通し、Seleniumの提供するWebDriverのAPIを叩く
- Driverが制御される
- ブラウザが制御される
という流れになります。
Seleniumがresponseメソッドを提供しない理由
本題になります。
詳しくはこちらのissueに書いてあります。(気になる方は読んでみてください) https://github.com/seleniumhq/selenium-google-code-issue-archive/issues/141
コメントを色々読んでみて、なるほどと思いました。一部を抜粋します。
Selenium does not provide this capability, as it is not something a user can do. (ユーザーが実行する可能性のある機能ではないため、Seleniumはこの機能を提供しません。)
とありました。Seleniumはあくまで「ユーザーの挙動」を提供するのですね。 レスポンスやヘッダーを見るのは、「ユーザーの挙動」ではなく、Seleniumが提供する機能のスコープ外だそうです。
結局responseメソッドを使うテストをどう解決したか?
該当するテストのみCapybaraのWebDriverをRack::Testに変更しました。(テスト対象はjsを使用していないため)
きちんとresponse_headersメソッドは機能しました🍎
しかし何故Rack::Testだとresponseメソッドを使用できるのかという疑問が残りました。
Rack::Testがどうやってresponseメソッドを提供しているのか
RackTestでは、ブラウザ操作をせずに(Seleniumを使わずに)E2Eテストを完結させています。
では、どうやってブラウザを介さずリクエストの発行とレスポンスの受け取りをやっているのでしょうか?
実際にRackのソースコードを見てみると、Rack::Testではrequestメソッドの戻り値にresponseのオブジェクトが設定されていることがわかりました。
RackのソースコードにRackTestのテストヘルパー(MockRequestといいます)
#Make a GET request and return a MockResponse. See #request. def get(uri, opts = {}) request(GET, uri, opts) end
参考: https://github.com/rack/rack/blob/master/lib/rack/mock.rb
Rack::Testはブラウザを介さないE2Eテストなので、requestメソッドの戻り値にresponseオブジェクトを設定し、擬似的にレスポンスを作成していたのでした。
最後に
最後まで読んでいただきありがとうございます。
今回、この記事を書くに当たって、SeleniumやWebDriverなどの関係性やRackに関して沢山調べたので、自分にとってこの辺の解像度が上がってよかったです。
個人的にはSeleniumを使用してE2Eテストを行うライブラリが沢山でているので、responseメソッドを提供してエンジニアの開発の手助けをしてもよいのではないか?と感じました。