PythonのSeleniumラッパーSeleneの使い方


概要

JavaのSeleniumWebDriverラッパーとしてはSelenideが有名ですが、PythonではSeleneがメジャーなようです。

公式のドキュメント等見てもイマイチわからないところが多かったので、自分で調べてみたついでにわかったことを残しておきます。

※色々調べてみた感想としては、Seleneの中身読むのが一番わかりやすかったです。

そもそもSelenとは

  • Effective web test automation toolである
    • 簡潔なAPI
    • 要素等の検索を待つ
    • アサートを待つ
    • 動的な要素にも対応
  • UIテストのロジックを自動化するもの

準備

手元の環境

  • Windows10Pro
  • Python 3.7.2
  • Selenium WebDriver 3.141.0
  • pytest 5.0.0
  • GoogleChrome 77
  • FireFox 70

環境構築

SeleniumWebDriverとPythonで自動テストを書ける状態に加えて、Seleneのインストールが必要です。

インストール手順などは公式に書いてあります。

yashaka/selene: Concise UI tests in Python + Ajax support + PageObjects

が、バージョンごとに複数手順があるようなので、今回は

latest published pre-release version (currently this is recommended option unless selene 1.0 will be released):

と書いてある最新のプレリリース版を使おうと思います。

$ pip install selene --pre

使い方

画面の要素を取得する

browser.element('#new-todo')のように書くことができますが、基本はより短いsを使います。

find_element_by_id("hoge")と書くかわりに、

  • s('#hoge')
  • s(by.id('hoge'))

のいずれかの書き方ができます。

一つめのほうは、sの引数にそのままCSSセレクタを書くパターン。もうひとつのほうは、byを使ってidなりxpathなりを使う、生のWebDriverに近いパターン。

いずれにせよ、行は減らないものの文字数はだいぶ減っています。ただ、読んで理解しやすいかというと怪しいかもしれませんね。

sは"search element"のsだそうです。

find_elements_by_hogeを行いたいときには、ssを使います。

ss('#todo-list>li')

こちらは、browser.all('#todo-list>li')とも書けますが、やはり短いssのほうを公式も勧めています。

スクリーンショット取得

生のWebDriverだとsave_screenshot()メソッドを使うところ、Seleneの場合はtake_screenshot()になるようです。

take_screenshot()はパスとファイル名の二つの引数をとり、どちらも書かなくても動くようにはなっています。

第一引数で指定するパスはスクリーンショットの保存先のパス、第二引数で指定するファイル名はスクリーンショットのファイル名です。

たとえば、

browser.take_screenshot(".\\capture")

とだけ書いた場合。この場合は、スクリプトと同階層にcaptureというフォルダができ、そこにスクリーンショットがどんどん追加されていきます。
このときのファイル名は過去のものと重複しないようにSeleneが付けてくれます。screen_1571995717156.pngのような名前になります。

引数に何も指定しないと、デフォルトのレポート保存先に"screenshots"というフォルダを作って保存されます。

私の手元の例は↓です。

C:\Users\yoshiki.itou\.selene\screenshots\1572231343309\screen_1572231343310.png

毎回保存先を指定するのが面倒な場合は、引数指定なしの場合の保存先を設定変更することができます。

config.reports_folder = ".//screenshot/"

と書くと、実行したテストスクリプトと同じ階層にscreenshotsというフォルダを作り、その中にスクリーンショットを保存していきます。

タイムアウトの設定

例えば
python
s("#new-todo").should_be(enabled)

とだけ書いた場合、デフォルトでタイムアウトは4秒。

s("#new-todo").should_be(enabled, timeout=10)

と書けば、この場合のタイムアウトが10秒になります。

もしくは

config.timeout = 10

と書いておけば、以降のタイムアウトが10秒になります。

実行するブラウザの指定

SeleneはAutomatic driver managementというのをやってくれるので、自分でダウンロードしてきたhogedriver.exeのパスを指定or環境変数PATHを通して・・・という作業をしなくて済むようになっています。

このへんはSergeyPirogov/webdriver_managerを利用しているようです。

デフォルトではChromeが起動するようになっているようですが、明示的に指定する場合は以下のように書きます。

from selene import config
from selene.browsers import BrowserName

config.browser_name = BrowserName.CHROME

FireFoxで動かしたい場合、CHROMEのところを単純に
Python
config.browser_name = BrowserName.FIREFOX

にすればうまくいくだろうと思ったのですが、私の環境ではエラーで動作せず。

中身見てみたところ、どうも

config.browser_name = BrowserName.MARIONETTE

だと動作するようでした。

※ここについては、みんなそうだという自信はないので、FIREFOXでダメなときはお試しください。

selene/browsers.py at master · yashaka/seleneあたりを見てみても、FIREFOX自体は設定できるので、ちょっと謎です。わかる方教えてください。

アサート

アサートに使えるものは

  • should
  • assure
  • should_be
  • should_have
  • should_not
  • assure_not
  • should_not_be
  • should_not_have

がありますが、内部的には

  • should
  • should_not

の2つで、他は上記いずれかのエイリアスです。

アサーションのためにshouldあるいはそのエイリアスを使うにあたって、PASS時は特に困ることはなさそうです。

一方FAIL時は多少厄介で、TimeOutExceptionが出てきてしまいました。

たとえば、以下がFAIL時の表示の例です。画面に51000と表示されるべきところ、わざと「41000と表示されるか」をテストして失敗させています。

コード(抜粋)

s(by.id("price")).should(have.text('41000'))

結果(抜粋)

E           selenium.common.exceptions.TimeoutException: Message:
E                       failed while waiting 4 seconds
E                       to assert Text
E                       for first_by('id', 'price')
E
E                       reason: ConditionMismatchException: condition did not match
E                               expected: 41000
E                                 actual: 51000
E                       screenshot: file://.//screenshot/screen_1572244618378.png

..\..\..\..\appdata\local\programs\python\python37\lib\site-packages\selene\elements.py:232: TimeoutException

ConditionMismatchExceptionなのでそこ見ればわかるだろと言われればそうなのですが、エラーメッセージの最後の最後でTimeoutExceptionで出てきてしまうのがうーん。。

とはいえ、比較のときには標準のassert使う、だとラッパーライブラリのうまみ半減なので、難しいところですね。

また、標準のassertだと条件式がFalseのときだけ表示するメッセージを第三引数に書いておけますが、Seleneのアサートだとそれが無さそうです。

参考