みなさん、こんにちは!
個人開発をしている南です!
今回は、SeleniumとPythonを使って自動テストを行っている時に、再帰処理を使って要素が表示されるまで待つ処理について紹介します。
今回の記事を読むことで、外部サービスの反応が悪くて途中で自動テストが止まってしまうということを防ぐヒントを掴むことができると思います。
再起処理をやろうと思った背景
Photo by Samuel Zeller on Unsplash
再帰処理をやろうと思った理由は、自動テストをする中でPayPalなどの外部サービスの反応が悪くてエラーになるということを解決したいと思ったことがきっかけでした。
再帰処理とは、自分自身を呼び出す関数が書かれていてある条件下で処理が終了するような処理のことをいいます。
ちなみに外部サービスはPayPalを使っているのですが、時々反応がめちゃくちゃ遅い時があります。
普通にテストしている時は、すぐにPayPalの決済画面をサクサク進んでくれるのですが、そうじゃない時があるわけです。
最初は、Pythonのsleep処理で10秒ほど待機する処理を書いていたのですが、反応が遅くて要素が見つからないと下記のようなエラーが出て途中で処理が止まってしまいます。
「no such element: Unable to locate element」
自動テストでいちいち止まってしまうのは、やめてほしいし、だからといって「WebDriverWait」処理だと待ってくれるのは1回だけなのでちゃんとした解決方法にはなりませんでした。
そこで、要素は遅れて表示されるだけなので、それなら表示されるまで待つ処理を書くために再帰処理を使って待とうということで再帰処理を使って解決することにしました。
ちなみに、再帰処理には使いすぎるとメモリ不足になるというデメリットがあるので、使う時は注意しましょう。
実際の処理
Photo by Lee Campbell on Unsplash
実際に動いているコードは下記のようなコードです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
def is_third_paypal_page_loaded(self): try: time.sleep(10) login_password_elem = self.driver.find_element_by_name( 'login_password' ) login_password_elem.clear() login_password_elem.send_keys( 'パスワード' ) btnLogin_elem = self.driver.find_element_by_name( 'btnLogin' ) except NoSuchElementException: print('no is_third_paypal_page_loaded') return self.is_third_paypal_page_loaded() else: print('is_third_paypal_page_loaded') return True is_thrid_result = self.is_third_paypal_page_loaded() if( is_thrid_result == True ): time.sleep(10) btnLogin_elem = self.driver.find_element_by_name( 'btnLogin' ) btnLogin_elem.click() |
上記コード上の「self.driver」の部分には、「webdriver.Chrome()」などの特定のドライバーがセットされています。
コードの説明
Photo by Ilya Pavlov on Unsplash
コードは次のような構成になっています。
1.処理を始める前に10秒待機。
2.inputのname属性を使って要素を取得します。
3,一度要素のテキストを削除します。
4.Seleniumのsend_keys関数を使って、取得した要素にパスワードを入力します。
5,次に進むといったボタンを取得するために、find_element_by_nameで要素を取得します。
6.この時に、PayPalのロードなどが原因で、次に進むボタンが見つからなかった時は、例外を発生させてもう一度処理を繰り返します。
7.ボタンを取得できたら、Trueを返して処理を抜けます。
8.Trueの処理をもらったら、一応Trueかどうかチェックして、また10秒待機します。
9.もう一度ボタンを取得します。
10.ボタンをクリックして、次に進みます。
処理を始める前に10秒待機について
処理を始める前に10秒待機する理由は、再帰処理が走りすぎるとメモリ不足になってしまうからです。
なので、Pythonのsleep処理で処理を始める前に10秒待機しています。
コード上だと下記の部分です。
1 |
time.sleep(10) |
inputのname属性を使って要素を取得について
ここでは、Seleniumの「find_element_by_name」を使ってinputのname属性を使って取得したい要素を取得しています。
他にも、idで取得したりもできます。
詳しくは下記のドキュメントを見ると色々載っています。
一度要素のテキストを削除について
再帰処理なので、テキストを削除せずに処理を走らせると前のテキストに+されて入力されます。
イメージ的には下記のような感じ。
「パスワードパスワードパスワード」
これだとログインの処理ができないので、テキストを削除してからテキストを入力という手順を踏んでいます。
コード上だと、下記の部分です。
1 |
login_password_elem.clear() |
要素名.clear()とすることで、削除したい要素のテキストを削除できます。
Seleniumのsend_keys関数を使って、取得した要素にパスワードを入力について
テキストの入力には、Seleniumの「send_keys」を使います。
コード上だと下記の部分です。
1 |
login_password_elem.send_keys( 'パスワード' ) |
要素名.send_keys(‘入力したいテキスト’)とすることで、入力したいテキストを指定した要素に入力できます。
次に進むといったボタンを取得するために、find_element_by_nameで要素を取得について
ここでは、find_element_by_nameを使って、次に進むためのボタンを取得しています。
コード上だと下記の部分です。
1 |
btnLogin_elem = self.driver.find_element_by_name( 'btnLogin' ) |
この時に、PayPalのロードなどが原因で、次に進むボタンが見つからなかった時は、例外を発生させてもう一度処理を繰り返しますについて
この処理では、Pythonの例外処理を使っています。
例外処理とは、コード上でエラーが出た時に処理を止めるのではなく、別の処理を動かすことができる処理のことを例外処理といいます。
コード上だと、下記のtry,except,elseの部分です。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
try: time.sleep(10) login_password_elem = self.driver.find_element_by_name( 'login_password' ) login_password_elem.clear() login_password_elem.send_keys( 'パスワード' ) btnLogin_elem = self.driver.find_element_by_name( 'btnLogin' ) except NoSuchElementException: print('no is_third_paypal_page_loaded') return self.is_third_paypal_page_loaded() else: print('is_third_paypal_page_loaded') return True |
このコードは簡略化すると次のような構成になっています。
1 2 3 4 5 6 7 |
try: #次に進むボタンが存在するかチェック except NoSuchElementException: #ボタンがなければ、例外処理を発生させてもう一度同じ処理を行う。 else: #ボタンが存在すれば、returnを使って処理を抜ける。 |
つまり、ボタンが見つかればTrueを返して、見つからなければもう一度同じ処理を行うという構造になっています。
ボタンを取得できたら、Trueを返して処理について
ここは上記で説明した通り、ボタンが見つかればTrueを返して再帰処理を止めます。
Trueの処理をもらったら、一応Trueかどうかチェックして、また10秒待機について
ここでは、再帰処理から帰ってきた値をチェックします。
何か別のことが原因で、Trueが返ってこなかった時にチェックをしています。
コード上だと、下記の部分です。
1 2 |
is_thrid_result = self.is_third_paypal_page_loaded() if( is_thrid_result == True ): |
もう一度ボタンを取得しますについて
一応念のため、find_element_by_nameを使ってもう一度要素を取得します。
コード上だと、下記の部分です。
1 2 |
time.sleep(10) btnLogin_elem = self.driver.find_element_by_name( 'btnLogin' ) |
ボタンをクリックして、次に進みますについて
最後にボタンが取得できたら、Seleniumのclickを使って要素をクリックして先の画面に進みます。
コード上だと、下記の部分です。
1 |
btnLogin_elem.click() |
要素名.clcik()とすると、指定した要素をクリックすることができます。
inmportする必要があるものについて
Photo by Alfons Morales on Unsplash
今回紹介した、time.sleepや例外処理などはそれぞれ下記のライブラリをインポートする必要があります。
1 2 3 |
from selenium import webdriver import time from selenium.common.exceptions import NoSuchElementException |
まとめ
今回は、SeleniumとPythonで再帰処理を行って要素が表示されるまで待機する処理を紹介しました。
この記事が僕と同じように、要素が見つかるまで待機する方法を探している方のお役にたれば嬉しいです。