【Ruby/RSpec】テスト時はプライベートモードでseleniumを動作させる

2020年10月29日Ruby

こんにちは、しきゆらです。
少し前、レンタルサーバの支払いを忘れていて一瞬サイトが消えていました。
自分でもびっくりしました・・・。

さて今回は、というか今回もRSpecのお話。
ここ最近、お仕事で自分の書いている小さなスクリプトにテストがなくて怖いなーという思いが強くなりRSpecを書くことが多いので、その時のメモです。

最近書いているのは、Seleniumでとあるサイトで行っている操作を自動化するというもの。
どうしても外部サイトをあれこれするスクリプトを書いているとテストを書きにくいです。
ということで、直接サイトにあれこれする部分は最低限ログインできるかくらいしかかけていません。
どうするのがいいんでしょうか。

さて、Seleniumでブラウザを制御するときに困るのは、キャッシュやCookieなどの影響で挙動が変わること。
ログインしようとしても前回の残骸が残っていると何度も確かめることができない、なんていうこともあります。
そこで、タイトルの通りテストの時はプライベートモードで動いてほしいなーと思っていい感じに書く方法をあれこれ考えてできたものをメモしておきます。


テスト対象となるスクリプト

まずは、ブラウザ操作をまとめる部分から。
Seleniumを初期化するときに、Chromeのオプションを渡してあげると、普段使っているプロファイルと分けて使うことができます。
また、実際に実行するときはキャッシュ等残っていても問題ないので、そのように設定してあります。

なお、Chromeに渡すことができるフラグは以下のサイトにまとまっています。
https://peter.sh/experiments/chromium-command-line-switches/

class Browser
  attr_reader :driver
  def initialize
    selenium_configures = [
      "--user-data-dir=selenium_profile", # プロファイルのパス:./selenium_profile
      "--profile-directory=Default", # プロファイルのデフォルトのものを使う
      "--restore-last-session" # 前回のセッションを保持する
    ]
    option = Selenium::WebDriver::Chrome::Options.new
    selenium_configures.each do |config|
      options.add_argument(config)
    end

    @driver = Selenium::WebDriver.for(:chrome, options: options)
  end
  # driverに対してごにょごにょする
end

ただ、こうしておくとオプションをテストの時に差し替えたりしにくいです。
ということで、オプションをメソッドに切り出します。

class Browser
  attr_reader :driver
  def initialize
    option = Selenium::WebDriver::Chrome::Options.new
    selenium_configures.each do |config|
      options.add_argument(config)
    end
   
   @driver = Selenium::WebDriver.for(:chrome, options: options)
  end
  
  # 設定部分を切り出した 
  def selenium_configures
    @selenium_configures ||=  [
     "--user-data-dir=selenium_profile", # プロファイルのパス:./selenium_profile
     "--profile-directory=Default", # プロファイルのデフォルトのものを使う
     "--restore-last-session" # 前回のセッションを保持する
    ]
  end
  # driverに対してごにょごにょする
end

こうしてあげると、RSpec側であれこれしやすくなります。

RSpec側のコード

プライベートモードでChromeを起動してあげるものと、そのほか諸々のオプションはこの辺。

[
   "--incognito",
   "--no-sandbox",
   "--disable-dev-shm-usage"
]

こいつらを、切り出したメソッドのように渡してあげたい。
それでは、RSpec側のコードを書いて見ます。
まずは、本チャンコードのselenium_configuresの値を無視するように変更します。

RSpec.describe Browser do
  let(:browser) { Browser.new }
  let(:options) { Selenium::WebDriver::Chrome::Options.new }
  before do
    # すべてのBrowserインスタンスが`selenium_configures`メソッドをもち、[]を返す
    allow_any_instance_of(Browser).to receive(:selenium_configures).and_return([])
  end
end

ひとまず、切り出したメソッドを無効化しています。
基本的にはselenium_configuresの返り値をプライベートモードの設定に置き換えればいいのですが、
それだけだと、必要ない場面や、ここではさらにheadlessにしたい、ということもあるかもしれないので以下のように付けはずしできるようにします。

RSpec.shared_context "private" do
  let(:options) { Selenium::WebDriver::Chrome::Options.new }
  let(:private_config) do
    [
      "--incognito",
      "--no-sandbox",
      "--disable-dev-shm-usage"
    ]   
  end
  
  before do
    private_config.each {|config| options.add_argument(config)}
  end
end

こんな感じで、Seleniumのオプションを設定する部分をshared_contextとしてワンセットにしておきます。
あとは、必要なところで`include_context “private"`のようにしてあげれば、必要な場面でプライベートモードで動かすことができるようになりました。

まとめ

今回は、Seleniumで良しななタイミングだけプライベートモードで動かしたいときにうまいことする方法を考えてみました。
いろいろやろうと思えばできるような環境はそろっているんだなぁという印象でした。
そのうち、ヘッドレス版とかもメモしたいと思います。

今回は、ここまで。
おわり

Posted by しきゆら