Seleniumを使ってCookieの読み書きを行うケースはよく見かけると思いますが、最近ではローカルストレージ(localStorage)の利用機会が増えてきたため、テストのためSeleniumからも操作したくなるケースが存在します。しかし、Seleniumでローカルストレージを直接扱う方法が提供されていないため、少し工夫が必要です。今回はその方法を解説します。
そもそもローカルストレージとは
ローカルストレージは「Web Storage」と呼ばれる規格の一種で、ブラウザにデータを保存するための仕組みです。データはJavaScriptを使って読み書きが行えます。
また、Web StorageにはsessionStorageとlocalStorageの2種類が用意されており、有効期限の違いなどはありますが、基本的な操作方法に違いはありません。具体的な違いは下記です。
データの有効期限 | データサイズ上限 | 別ウィンドウでの共有 | |
sessionStorage | ウィンドウやタブを閉じると削除される | 1オリジンごとに5MB | 不可 |
localStorage | 永続的(明示的に削除するまで残る) | 1オリジンごとに5MB | 可 |
※表中の「オリジン」とはURLスキーム(http,httpsなど)+ドメイン+ポートを組み合わせたもので、これらが全て同じであれば1オリジンとして扱われます。
Cookieとの違い
データをブラウザに保存する技術で似たようなものとして「Cookie」が存在します。データを保存するという意味では同じですが、下記の点で大きく異なります。
- 保存できる容量が4KBと小さい
- データの作成はサーバーが行う
- 勝手にサーバーに送られる
- 有効期限を自由に設定できる
Cookieは技術として古来より存在し、多くのサイトで利用されています。データの作成はサーバー側で行われ、ブラウザは送られてきた情報を勝手に保存し、勝手に送信するという点でサービス提供側からすると非常に使い勝手の良い仕組みとなっています。しかし、昨今のWebサービスの複雑化により、4KBという保存容量では足りないケースや、データをクライアント側の任意のタイミングで送るようにコントロールしたいようなケースではWebストレージが好んで使われます。
Seleniumでローカルストレージを扱う方法
SeleniumでCookieを取得する場合、getCookies()という便利なメソッドが用意されていますが、ローカルストレージの場合はそうはいきません。代わりに何を使うかというと「execute_script()」を使います。
ローカルストレージへのアクセスはこちらにあるコードを参考にさせていただきました。このコードに加えて保存、復元するためのコードが下記となります。
ローカルストレージにアクセスするためのクラス
class LocalStorage:
def __init__(self, driver) :
self.driver = driver
def __len__(self):
return self.driver.execute_script("return window.localStorage.length;")
def items() :
return self.driver.execute_script( \
"var ls = window.localStorage, items = {}; " \
"for (var i = 0, k; i < ls.length; ++i) " \
" items[k = ls.key(i)] = ls.getItem(k); " \
"return items; ")
def keys() :
return self.driver.execute_script( \
"var ls = window.localStorage, keys = []; " \
"for (var i = 0; i < ls.length; ++i) " \
" keys[i] = ls.key(i); " \
"return keys; ")
def get(self, key):
return self.driver.execute_script("return window.localStorage.getItem(arguments[0]);", key)
def set(self, key, value):
self.driver.execute_script("window.localStorage.setItem(arguments[0], arguments[1]);", key, value)
def has(self, key):
return key in self.keys()
def remove(self, key):
self.driver.execute_script("window.localStorage.removeItem(arguments[0]);", key)
def clear(self):
self.driver.execute_script("window.localStorage.clear();")
def __getitem__(self, key) :
value = self.get(key)
if value is None :
raise KeyError(key)
return value
def __setitem__(self, key, value):
self.set(key, value)
def __contains__(self, key):
return key in self.keys()
def __iter__(self):
return self.items().__iter__()
def __repr__(self):
return self.items().__str__()
execute_script()を使ってJavaScriptを直接実行し、ローカルストレージにアクセスしているのが分かりますね。
保存・復元する方法
保存・復元にはpickleを使います。Pythonでオブジェクトをシリアライズして保存するのに便利だからです。Pythonで扱えるデータ形式であればなんでも良いので、DBでもCSVでも形式は問いません。
保存方法
driver = webdriver.Chrome(ChromeDriverManager().install())
storage = LocalStorage(driver)
driver.get("{{URL}}")
data = {}
for key, value in storage.items().items():
data[key] = value
pickle.dump(data, open("./local_storage.pkl", "wb"))
driver.get({{URL}})としてやらないとデータを保存できません(読み込みも同じ)。1オリジンを特定するため、この処理を呼び出しておく必要があるのだと思います。
あとはLocalStorageクラスを介してデータを取得し、pickleで保存するだけです。
復元方法
driver = webdriver.Chrome(ChromeDriverManager().install())
storage = LocalStorage(driver)
driver.get("{{URL}}")
if os.path.exists("./local_storage.pkl"):
data = pickle.load(open("./local_storage.pkl", "rb"))
if len(data) > 0:
for key, value in data.items():
storage.set(key, value)
復元方法も保存の時と同じように「driver.get({{URL}})」を呼び出してやる必要があります。
あとは保存したデータをpickleでオブジェクトに戻し、LocalStorageクラスを使って1件ずつローカルストレージに書き込みます。
最後に
やることはたったこれだけでシンプルですね。汎用的に全件取得と保存ができるので、多くのシチュエーションで利用可能なコードになっていると思います。Seleniumを使ったE2Eテストでは、テストケース毎にデータを用意する必要がありますが、pickleだとバイナリ形式で扱いづらいため、CSVなどテキスト形式でデータを用意しておくのが良いでしょう。そうすれば、通常なら発生させづらい異常値などもローカルストレージに保存させられるため、テストケースとして役立つはずです。
ローカルストレージに自由にアクセスできるとテストの幅も広がって楽しいですね。それでは、また。