Seleniumで明示的に要素検索の待ち時間を設定する

この記事は よちよち.rb Advent Calendar 2014 10日目の記事です。 9日目は5t111111さんによる審判の雷 Lightning Pickerでした。

自分の妄想を思わずアプリにしてしまうような人に私も成りたいです!

 今回書くこと

社内でSeleniumおじさんになることを目指して日々格闘していますので、その内容を記録したいと思います。

テーマとしては、「要素を探しにいく待ち時間について」です。

具体的には、データの入力を繰り返す処理をしている中で、データによって確認アラートが出るときと出ないときがあり、出るときは閉じたいのですが、出ているかの確認で要素(確認アラート)を探している時間がいちいち長かったので短くしたい、という内容になります。伝わりますかね・・・

ただ、短くしたいというのは、イレギュラーケースな気がしていまして(というかその目的の実現はあまり綺麗には出来ていないと反省中なので)、特定の処理だけ長く待ちたいとかに使うのが多いみたいです。そういう例が多かったです。画像重たいので、長めに待ちたい、とかですね。

ちなみに、Rubyはcapybaraを使うことが多いためなのか、selenium webdriverを使っているサンプルがあまり見つけられない気がします。Javaとかの例が多い気がします。私もcapybaraを使えば良い、ということかもしれません。

参考にしたもの

実践 Selenium WebDriver

実践 Selenium WebDriver

動作環境

やり方

Selenium::WebDriver::Waitというクラスがあり、このクラスが持つuntilメソッドを使いました。 untilメソッドは引数にブロックを取り、ブロックの返り値がtrueになるまでブロックの処理を繰り返すようです。

Selenium::WebDriver::Waitのnewで待ち時間を設定出来るので、この待ち時間だけブロック処理を繰り返して、falseが続く場合には最後はタイムアウトエラーが発生します。

例えば、

wait = Selenium::WebDriver::Wait.new({timeout: 10})
wait.until do
  false
end

上記例では、ずっとfalseが返るので、10秒経過するとタイムアウトエラーになります。

APIリファレンスで、untilのソースを見てみると、

# File 'rb/lib/selenium/webdriver/common/wait.rb', line 33
def until(&blk)
  end_time = Time.now + @timeout
  last_error = nil

  until Time.now > end_time
    begin
      result = yield
      return result if result
    rescue *@ignored => last_error
      # swallowed
    end

    sleep @interval
  end
# 以下略

となってました。return result if resultとありますので、とにかくブロックの結果がtrue(truthy)ならそのまま結果を返してしまうみたいですね。逆に、繰り返すためには、ブロックの結果がfalseにならないといけないみたいです。

なので、私は、

wait = Selenium::WebDriver::Wait.new({timeout: 0.5})
begin
  wait.until do
    begin
      # 要素があったら実行するよという処理
    rescue
      false
    end
  end
rescue
  # タイムアウトは揉み消す
end

のような形で、処理短縮を目指しました。(この場合、最初の処理の時間が長かったら意味がないと思いますが) ちょっとゴリゴリ感が否めないので、きっともっと良い書き方があるのではと模索中です。


明日のよちよち.rb Advent Calendar 2014は、waterlow さんの「私のよちよち.rb活用法」です!気になるタイトルですね~