Playwright中如何保持登錄狀態(tài)
引言
在編寫UI自動化測試用例的時候,通常會采用每個測試用例前打開新頁面重新進行登錄,以減少用例間的影響,比如一個測試用例執(zhí)行失敗會影響到下一個測試用例的執(zhí)行,或者下一個用例的開始依賴于上一個用例的結(jié)束頁面。但是這種方式會使得測試用例的執(zhí)行時間大幅度上升,尤其是在測試用例劃分的顆粒度比較小的時候;加入一個項目中有2000個測試用例,登錄操作耗時2秒,那么光耗費在登錄上面的時間就有4000秒,達到一個多小時了,嚴重影響測試執(zhí)行效率,再假如項目中使用了驗證碼限制登錄的情況,那么就更復(fù)雜了。所以,如果可以將登錄狀態(tài)保持住,開始測試用例的時候打開新頁面,跳過登錄直接進入到待測系統(tǒng)中,就可以大幅度提高測試執(zhí)行效率。通常,每種UI自動化測試工具都會有類似的功能,這里以Playwright為例來介紹如何實現(xiàn)。
功能實現(xiàn)
在Playwright中提供了現(xiàn)成的方法,通過 context.storage_state(path='<文件路徑>') ,可以將當(dāng)前瀏覽器上下文的全部狀態(tài)保存下來,在創(chuàng)建瀏覽器上下文時,添加 storage_state 參數(shù)即可讀取保存的文件,從而完全恢復(fù)之前的瀏覽器狀態(tài)。示例代碼如下
# 首次登陸系統(tǒng)
from playwright.sync_api import sync_playwright
with sync_playwright() as playwright:
browser = playwright.chromium.launch()
context = browser.new_context()
page = context.new_page()
# 登陸系統(tǒng)
page.goto('<login url>')
page.fill('<username>', '<username selector>')
page.fill('<password>', '<password selector>')
page.click('<login button selector>')
# 判斷是否登陸成功
assert 'Welcome' in page.title()
# 保存狀態(tài)文件
context.storage_state(path='login_data.json')
# 使用已保存的狀態(tài)文件跳過登錄狀態(tài)直接訪問系統(tǒng)
with sync_playwright() as playwright:
browser = playwright.chromium.launch()
# 創(chuàng)建瀏覽器上下文時加載狀態(tài)文件
context = browser.new_context(storage_state='login_data.json')
page = context.new_page()
# 直接訪問登錄后的URL
page.goto('<welcome url>')
# 判斷是否訪問到登錄后的頁面
assert 'Welcome' in page.title()
結(jié)合Pytest
通過以上代碼驗證了這種實現(xiàn)方式的可用性,還是很好用的。結(jié)合Pytest測試框架,可以通過fixture的形式提供一個已登錄的page對象,可以直接在測試用例中使用,實現(xiàn)方式如下:
@pytest.fixture()
def logged_page():
ss_file = 'login_data.json'
with sync_playwright() as playwright:
browser = playwright.chromium.launch()
# 判斷是否存在狀態(tài)文件,有的話就加載
if os.path.isfile(ss_file):
context = browser.new_context(storage_state=ss_file)
else:
context = browser.new_context()
page = context.new_page()
# 直接跳轉(zhuǎn)至登錄后頁面,前提是未登錄用戶訪問待測系統(tǒng)會自動跳轉(zhuǎn)至登錄頁面,如果你的系統(tǒng)邏輯不一樣,需要修改
page.goto('<welcome url>')
# 通過title判斷是否成功進入系統(tǒng),如果沒有需要進行登錄,一般在第一次訪問系統(tǒng),或登錄信息過期等原因會觸發(fā)
if 'Welcome' not in page.title():
page.fill('<username>', '<username selector>')
page.fill('<password>', '<password selector>')
page.click('<login button selector>')
yield page
# 測試執(zhí)行結(jié)束后保存狀態(tài)文件,前提是測試用例中不能退出系統(tǒng),安全起見加上異常處理
try:
context.storage_state(path=ss_file)
except Exception as e:
print(e)
結(jié)合Clent-Page Object模式
如前,結(jié)合Pytest已經(jīng)可以實現(xiàn)一個很好用的fixture,在很多場景下已經(jīng)夠用了,不過我在項目中結(jié)合Clent-Page Object模式進行了另一種實現(xiàn),核心代碼如下:
class Client(ABC):
playwright = None
browser = None
def __init__(self, url: str, *, storage_state_name: str = None):
self.url = url
self.context = None
self.main_page = None
self.storage_state_file_path = None
if storage_state_name is not None:
self.storage_state_file_path = os.path.join('storage_state', f'{storage_state_name}.json')
@abstractmethod
def register_page(self):
pass
@abstractmethod
def login(self, **kwargs):
pass
@abstractmethod
def is_logged(self):
pass
def start(self) -> None:
Client.playwright = sync_playwright().start()
Client.browser = Client.playwright.chromium.launch(**BROWSER_CONFIG)
if self.storage_state_file_path is not None and os.path.isfile(self.storage_state_file_path):
self.context = Client.browser.new_context(**CONTEXT_CONFIG, storage_state=self.storage_state_file_path)
else:
self.context = Client.browser.new_context(**CONTEXT_CONFIG)
self.main_page = self.context.new_page()
self.main_page.goto(self.url)
self.register_page()
def close(self) -> None:
if self.storage_state_file_path is not None:
if not os.path.exists('storage_state'):
os.mkdir('storage_state')
self.context.storage_state(path=self.storage_state_file_path)
self.main_page.close()
self.context.close()
def logged_user(client, url, *, scope = 'function', storage_state_name = None, **kwargs):
@pytest.fixture(scope=scope)
def user_fixture():
user = client(url, storage_state_name=storage_state_name)
user.start()
logger.info(f'用戶登錄: {kwargs}')
if not user.is_logged():
user.login(**kwargs)
yield user
user.close()
繼承Client類,使用 logger_user 函數(shù)來生成不同終端用戶的fixture,以實現(xiàn)繼承Pytest。
到此這篇關(guān)于Playwright中如何保持登錄狀態(tài)的文章就介紹到這了,更多相關(guān)Playwright保持登錄狀態(tài)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
深度解析Django REST Framework 批量操作
這篇文章主要介紹了深度解析Django REST Framework批量操作,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-05-05
pytorch使用tensorboardX進行l(wèi)oss可視化實例
今天小編就為大家分享一篇pytorch使用tensorboardX進行l(wèi)oss可視化實例,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-02-02
python redis 批量設(shè)置過期key過程解析
這篇文章主要介紹了python redis 批量設(shè)置過期key過程解析,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2019-11-11
Python中print函數(shù)語法格式以及各參數(shù)舉例詳解
這篇文章主要給大家介紹了關(guān)于Python中print函數(shù)語法格式以及各參數(shù)舉例詳解的相關(guān)資料,print()函數(shù)用于將指定的字符串或?qū)ο?通常是字符串)輸出到屏幕或文件中,需要的朋友可以參考下2023-10-10
Python?EasyDict庫以屬性方式訪問字典元素(無需使用方括號和鍵)
在Python中,字典(dict)是一種常用的數(shù)據(jù)結(jié)構(gòu),用于存儲鍵值對,然而,有時候我們希望以屬性的方式訪問字典中的元素,而無需使用方括號和鍵,這就是EasyDict庫的用武之地,本文將深入介紹EasyDict庫,展示其強大的功能和如何通過示例代碼更好地利用它2023-12-12

