Playwright中如何保持登錄狀態(tài)
引言
在編寫UI自動(dòng)化測試用例的時(shí)候,通常會(huì)采用每個(gè)測試用例前打開新頁面重新進(jìn)行登錄,以減少用例間的影響,比如一個(gè)測試用例執(zhí)行失敗會(huì)影響到下一個(gè)測試用例的執(zhí)行,或者下一個(gè)用例的開始依賴于上一個(gè)用例的結(jié)束頁面。但是這種方式會(huì)使得測試用例的執(zhí)行時(shí)間大幅度上升,尤其是在測試用例劃分的顆粒度比較小的時(shí)候;加入一個(gè)項(xiàng)目中有2000個(gè)測試用例,登錄操作耗時(shí)2秒,那么光耗費(fèi)在登錄上面的時(shí)間就有4000秒,達(dá)到一個(gè)多小時(shí)了,嚴(yán)重影響測試執(zhí)行效率,再假如項(xiàng)目中使用了驗(yàn)證碼限制登錄的情況,那么就更復(fù)雜了。所以,如果可以將登錄狀態(tài)保持住,開始測試用例的時(shí)候打開新頁面,跳過登錄直接進(jìn)入到待測系統(tǒng)中,就可以大幅度提高測試執(zhí)行效率。通常,每種UI自動(dòng)化測試工具都會(huì)有類似的功能,這里以Playwright為例來介紹如何實(shí)現(xiàn)。
功能實(shí)現(xiàn)
在Playwright中提供了現(xiàn)成的方法,通過 context.storage_state(path='<文件路徑>') ,可以將當(dāng)前瀏覽器上下文的全部狀態(tài)保存下來,在創(chuàng)建瀏覽器上下文時(shí),添加 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)建瀏覽器上下文時(shí)加載狀態(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
通過以上代碼驗(yàn)證了這種實(shí)現(xiàn)方式的可用性,還是很好用的。結(jié)合Pytest測試框架,可以通過fixture的形式提供一個(gè)已登錄的page對象,可以直接在測試用例中使用,實(shí)現(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)會(huì)自動(dòng)跳轉(zhuǎn)至登錄頁面,如果你的系統(tǒng)邏輯不一樣,需要修改 page.goto('<welcome url>') # 通過title判斷是否成功進(jìn)入系統(tǒng),如果沒有需要進(jìn)行登錄,一般在第一次訪問系統(tǒng),或登錄信息過期等原因會(huì)觸發(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)可以實(shí)現(xiàn)一個(gè)很好用的fixture,在很多場景下已經(jīng)夠用了,不過我在項(xiàng)目中結(jié)合Clent-Page Object模式進(jìn)行了另一種實(shí)現(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,以實(shí)現(xiàn)繼承Pytest。
到此這篇關(guān)于Playwright中如何保持登錄狀態(tài)的文章就介紹到這了,更多相關(guān)Playwright保持登錄狀態(tài)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
python 根據(jù)網(wǎng)易云歌曲的ID 直接下載歌曲的實(shí)例
今天小編就為大家分享一篇python 根據(jù)網(wǎng)易云歌曲的ID 直接下載歌曲的實(shí)例,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2019-08-08Python腳本實(shí)現(xiàn)網(wǎng)卡流量監(jiān)控
這篇文章主要介紹了Python腳本實(shí)現(xiàn)網(wǎng)卡流量監(jiān)控,本文直接給出實(shí)現(xiàn)代碼,需要的朋友可以參考下2015-02-02Python函數(shù)默認(rèn)參數(shù)常見問題及解決方案
這篇文章主要介紹了Python函數(shù)默認(rèn)參數(shù)常見問題及解決方案,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-03-03Python+wxPython實(shí)現(xiàn)一個(gè)簡單的音樂播放器
這篇文章主要為大家詳細(xì)介紹了如何使用Python編程語言和wxPython模塊創(chuàng)建一個(gè)簡單的音樂播放器,文中的示例代碼講解詳細(xì),感興趣的可以了解下2023-09-09Pytorch中實(shí)現(xiàn)只導(dǎo)入部分模型參數(shù)的方式
今天小編就為大家分享一篇Pytorch中實(shí)現(xiàn)只導(dǎo)入部分模型參數(shù)的方式,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-01-01Java?超詳細(xì)講解核心類Spring?JdbcTemplate
JdbcTemplate?JdbcTemplate是Spring?JDBC核心包(core)中的核心類,它可以通過配置文件、注解、Java?配置類等形式獲取數(shù)據(jù)庫的相關(guān)信息,實(shí)現(xiàn)了對JDBC開發(fā)過程中的驅(qū)動(dòng)加載、連接的開啟和關(guān)閉、SQL語句的創(chuàng)建與執(zhí)行、異常處理、事務(wù)處理、數(shù)據(jù)類型轉(zhuǎn)換等操作的封裝2022-04-04