こんにちは。今回は自動テスト開発においてデバッグをした(そして諦めた)際のログを残してみようと思います。
SETエンジニアがどんなことを考えて試行錯誤しているのかトレースできるようにしてみたので、一種の読み物としてお楽しみいただければと思います。
サマリ
- Selenium 4からdesired_capabilitiesがDeprecatedになり、Optionsを使うべきとWarningが出るようになった
- Safari DriverでiOS Safariを動かそうとOptionsを変えて試行錯誤したものの、iOSでなくPCのSafariが起動してしまう
- 調べた結果バグっぽいので、解消されるまで諦めてdesired capabilitiesを使うことにした
Environment
- Python 3.10.0
- selenium 4.1.0
Before Selenium 4
SeleniumではSafari Driverをサポートしており、
これを利用するとMac SafariだけでなくiOS Safariも動かすことができます。
SeleniumのPythonライブラリを使って、
pytest向けのテストスクリプトとして記述をすると以下のようになります。
from selenium import webdriver
class TestCase:
def test_safari(self):
desired_caps = {
'browserName': 'safari',
'platform': 'ios',
}
driver = webdriver.Safari(desired_capabilities=desired_caps)
driver.get("https://google.co.jp")
driver.quit()
※下記記事を参考にしました。
After Selenium 4
Selenium 4から、desired_capabilities
がdeprecatedとなり、
上記コードを実行すると以下のようなWarningが出るようになりました。
DeprecationWarning: desired_capabilities has been deprecated, please use the Options class to set it
driver = webdriver.Safari(desired_capabilities=desired_caps)
バージョンアップ対応もQAの仕事なので、これをどう改善したらいいか検討してみます。
Selenium 4 with Options
Warningには「Options
クラスを使え」とあるので、SafariのOptions
クラスを引っ張ってきます。
SafariのOptions
クラスは以下の場所にあります。
from selenium.webdriver.safari.options import Options as SafariOptions
これをインスタンス化し、vars()
関数を使ってデフォルト値を見ると下記のようになっていました。
{
'_caps': { 'browserName': 'safari', 'platformName': 'mac', 'pageLoadStrategy': 'normal' },
'mobile_options': None,
'_arguments': [],
'_ignore_local_proxy': False,
'_binary_location': None,
'_preferences': {},
'log': < selenium.webdriver.safari.options.Log object at 0x1072e4910 >
}
なるほど、どうも'_caps'
がdesired_capabilities
に相当するようですね。
またOptions
には'_caps'
をsetするメソッドであるset_capability()
も用意されていました。
つまり、これを用いて先ほどのコードを書き換えると下記のようになるはずです。
from selenium import webdriver
from selenium.webdriver.safari.options import Options as SafariOptions
class TestCase:
def test_safari_new(self):
options = SafariOptions()
options.set_capability("platform", "ios")
options.set_capability("platformName", "ios") # to overwrite mac
driver = webdriver.Safari(options=options)
driver.get("https://google.co.jp")
しかしこれを実行すると、何故かiOSではなくMacのSafariが起動しました。
Debugging
なんでじゃい、と思ってGitHubのソースコードを読んでみると…
class WebDriver(RemoteWebDriver):
"""
Controls the SafariDriver and allows you to drive the browser.
"""
def __init__(self, port=0, executable_path=DEFAULT_EXECUTABLE_PATH, reuse_service=False,
desired_capabilities=DEFAULT_SAFARI_CAPS, quiet=False,
keep_alive=True, service_args=None, options: Options = None, service: Service = None):
desired_capabilities
にはデフォルト値DEFAULT_SAFARI_CAPS
が渡されていて、options
にはNoneが渡されていることが分かりました。
となると、desired_capabilities
がoptions
より内部での処理の優先順位が高い場合、
自分で設定したoptions
内の_caps
よりもDEFAULT_SAFARI_CAPS
が優先されてしまうことになります。
もしもDEFAULT_SAFARI_CAPS
がMac Safariを起動するような設定になっていた場合、先ほどの挙動が引き起こされます。
これを検証するためには、Warningは出るものの起動はするtest_safari
を以下のように書き換えて、MacとiOSのどちらのSafariが起動するか確認すればよさそうです。
from selenium import webdriver
from selenium.webdriver.safari.options import Options as SafariOptions
class TestCase:
def test_safari_debugging(self):
options = SafariOptions() # optionsはMac Safari を起動するように指定
desired_caps = {
'browserName': 'safari',
'platform': 'ios',
} # desired_capsはiOS Safariを起動するように指定
driver = webdriver.Safari(options=options, desired_capabilities=desired_caps)
driver.get("https://google.co.jp")
driver.quit()
これを実行すると予想通りiOS Safariが起動しました。
さて、DEFAULT_SAFARI_CAPS
の実体は…
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
DEFAULT_SAFARI_CAPS = DesiredCapabilities.SAFARI.copy()
であって、この中身を見ると以下であることが分かります。
{
'browserName': 'safari',
'platformName': 'mac'
}
ビンゴっぽい。
これらのことから、先ほどの推論通り、
options
に適切なパラメータを設定して渡しても無視されてMac Safariが起動してしまうのは、
内部でoptions
よりdesired_capabilities
が優先して処理されるのにも関わらず、
desired_capabilities
にDEFAULT_SAFARI_CAPS
が設定されているからのようであることが分かりました。
Reporting
とはいえ、これは観測した一部分の結果からの推論にすぎません。詳細は開発者の方に確認していただく必要があります。
例えば、お気づきの方もいたかと思いますが、SafariOptions
の中に存在している'mobile_options': None
の記述はやはり気になりますよね。どうも中身を見る限りAndroidに関する設定項目のようだったので今回はスルーしましたが、これを適切に設定したら上手くいくという可能性もあります。
本来はここで発生した事象と再現手順と原因の推論を書いたIssueを上げてやっとQAの仕事が終わりになります。
…が、今回はWarningを無視すれば実用上の問題はないことと、他の業務があるという世知辛い理由でそこまではやれていません。尻切れトンボですみません。
おわりに
ということで、今回はOSSにたまたまバグっぽいものが見つかったので、デバッグ作業を追体験できるような記事を書いてみました。
僕たちSETエンジニアは、普段はこんなことをしながら、動かねー動かねー動かねー・・・と99回くらい繰り返したあとでやっと動いた、みたいなことを繰り返していると知っていただけると嬉しいです。自動化 is 大変。ではまた。