40. 自動化異步測試開發之編寫異步業務函數、測試函數和測試類(類寫法)
一、類結構設計解析
1.1 基類設計
class Base:async_driver = None # 🚗 存儲瀏覽器驅動實例async def get(self, url: str = 'http://secure.smartbearsoftware.com/...'):await self.async_driver.get(url) # 🌐 導航到指定URL
- 核心成員:
async_driver
存儲瀏覽器驅動實例 - 通用功能:提供頁面導航方法
- 默認URL:Web Orders登錄頁面
1.2 登錄頁面類
class LoginPage(Base):async def login(self, username: str = 'Tester', password: str = 'test'):# 輸入用戶名await self.async_driver.send_keys('id', 'ctl00_MainContent_username', text=username)# 輸入密碼await self.async_driver.send_keys('id', 'ctl00_MainContent_password', text=password)# 點擊登錄按鈕await self.async_driver.click('name', 'ctl00$MainContent$login_button')
- 功能定位:封裝登錄相關操作
- 默認憑證:用戶名’Tester’,密碼’test’
- 元素定位:使用ID定位輸入框,Name定位按鈕
1.3 主頁面類
class MainPage(LoginPage):async def search(self):# 點擊搜索菜單await self.async_driver.click('xpath', '//*[@id="ctl00_menu"]/li[3]/a')# 輸入搜索內容(需要添加text參數)await self.async_driver.send_keys('id', 'ctl00_MainContent_fmwOrder_txtName')# 點擊搜索按鈕await self.async_driver.click('id', 'ctl00_MainContent_fmwOrder_InsertButton')async def logout(self):# 點擊登出鏈接await self.async_driver.click('xpath', '//*[@id="ctl00_logout"]')
- 繼承關系:繼承自LoginPage,復用登錄功能
- 擴展功能:添加搜索和登出操作
- 元素定位:混合使用XPath和ID定位器
二、測試類實現
2.1 登錄測試類
class AsyncTestLogin(MainPage):async def test_login(self, *args):await self.get() # 打開登錄頁await self.login(*args) # 執行登錄# 驗證登錄成功title_text = await self.async_driver.text('xpath', '//*[@id="aspnetForm"]//td[1]/h1')assert title_text == 'Web Orders'
- 測試流程:
- 打開登錄頁
- 執行登錄
- 驗證頁面標題
- 斷言驗證:檢查標題是否為’Web Orders’
2.2 搜索測試類
class AsyncTestMain(MainPage):async def test_search(self):await self.get() # 打開登錄頁await self.login() # 登錄系統await self.search() # 執行搜索# 驗證錯誤提示error_msg = await self.async_driver.text('id', "ctl00_MainContent_fmwOrder_RequiredFieldValidator3")assert error_msg == "Field 'Street' cannot be empty."await self.logout() # 退出登錄
- 測試流程:
- 登錄系統
- 執行搜索
- 驗證錯誤提示
- 登出系統
- 斷言驗證:檢查是否顯示街道字段不能為空的錯誤
三、完整測試執行示例
3.1 測試運行器
import asyncio
from chap9.async_browser import AsyncBrowser
from aiohttp import ClientSessionasync def run_tests():async with ClientSession() as session:# 啟動瀏覽器async with AsyncBrowser.start(remote_driver_server='http://localhost:9515',capabilities={'browserName': 'chrome'},http_session=session) as driver:# 創建測試實例login_test = AsyncTestLogin()login_test.async_driver = driversearch_test = AsyncTestMain()search_test.async_driver = driver# 執行登錄測試print("執行登錄測試...")await login_test.test_login()print("登錄測試通過 ?")# 執行搜索測試print("執行搜索測試...")await search_test.test_search()print("搜索測試通過 ?")if __name__ == "__main__":asyncio.run(run_tests())
3.2 預期執行結果
執行登錄測試...
登錄測試通過 ?
執行搜索測試...
搜索測試通過 ?
3.3 實際操作流程
登錄測試:1. 打開登錄頁2. 輸入用戶名:Tester3. 輸入密碼:test4. 點擊登錄按鈕5. 驗證頁面標題:Web Orders搜索測試:1. 打開登錄頁2. 輸入憑證登錄3. 點擊搜索菜單4. 點擊搜索按鈕(不輸入內容)5. 驗證錯誤提示:Field 'Street' cannot be empty.6. 點擊登出鏈接
四、類寫法的優勢分析
4.1 繼承結構
Base│├── LoginPage│ ││ └── MainPage│ ││ ├── AsyncTestLogin│ ││ └── AsyncTestMain
- 代碼復用:通過繼承復用公共方法和屬性
- 功能擴展:子類可以擴展或重寫父類方法
- 邏輯分層:清晰區分頁面操作和測試驗證
4.2 與函數寫法的對比
特性 | 類寫法 | 函數寫法 |
---|---|---|
狀態管理 | 通過類屬性維護狀態 | 依賴參數傳遞狀態 |
代碼組織 | 按頁面/功能模塊組織 | 按操作流程組織 |
復用性 | 高(繼承機制) | 中(函數組合) |
學習曲線 | 較陡峭(需理解OOP) | 較平緩 |
適用場景 | 大型項目/復雜頁面 | 小型項目/簡單流程 |
五、最佳實踐建議
5.1 類設計優化
class BasePage:def __init__(self, driver):self.driver = driver # ? 通過構造器注入驅動async def open(self, url):await self.driver.get(url)class LoginPage(BasePage):USERNAME = ('id', 'ctl00_MainContent_username')PASSWORD = ('id', 'ctl00_MainContent_password')LOGIN_BTN = ('name', 'ctl00$MainContent$login_button')async def login(self, username, password):await self.driver.send_keys(*self.USERNAME, text=username)await self.driver.send_keys(*self.PASSWORD, text=password)await self.driver.click(*self.LOGIN_BTN)
5.2 測試類優化
class TestLogin(LoginPage):TITLE = ('xpath', '//*[@id="aspnetForm"]//td[1]/h1')async def test_success_login(self):await self.open(LOGIN_URL)await self.login(TEST_USER, TEST_PASS)assert await self.driver.text(*self.TITLE) == 'Web Orders'
5.3 執行入口優化
async def main():driver = await create_driver()login_page = LoginPage(driver)await login_page.test_success_login()
這種基于類的異步測試開發模式,通過面向對象的設計思想,提供了更結構化、可維護性更高的測試代碼組織方式,特別適合中大型自動化測試項目。
六、完整代碼
"""
Python :3.13.3
Selenium: 4.31.0async_test_cls.py
"""class Base:async_driver = Noneasync def get(self, url: str = 'http://secure.smartbearsoftware.com/samples/testcomplete12/WebOrders/Login.aspx'):await self.async_driver.get(url)class LoginPage(Base):async def login(self, username: str = 'Tester', password: str = 'test'):await self.async_driver.send_keys('id', 'ctl00_MainContent_username', text=username)await self.async_driver.send_keys('id', 'ctl00_MainContent_password', text=password)await self.async_driver.click('name', 'ctl00$MainContent$login_button')class MainPage(LoginPage):async def search(self):await self.async_driver.click('xpath', '//*[@id="ctl00_menu"]/li[3]/a')await self.async_driver.send_keys('id', 'ctl00_MainContent_fmwOrder_txtName')await self.async_driver.click('id', 'ctl00_MainContent_fmwOrder_InsertButton')async def logout(self):await self.async_driver.click('xpath', '//*[@id="ctl00_logout"]')class AsyncTestLogin(MainPage):async def test_login(self, *args):await self.get()await self.login(*args)assert await self.async_driver.text('xpath', '//*[@id="aspnetForm"]//td[1]/h1') == 'Web Orders'class AsyncTestMain(MainPage):async def test_search(self, text: str = '1'):await self.get()await self.login()await self.search(text)assert await self.async_driver.text('id',"ctl00_MainContent_fmwOrder_RequiredFieldValidator3") == "Field 'Street' cannot be empty."await self.logout()
「小貼士」:點擊頭像→【關注】按鈕,獲取更多軟件測試的晉升認知不迷路! 🚀