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 ClientSession
async 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 = driver
search_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. 输入用户名:Tester
3. 输入密码:test
4. 点击登录按钮
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.0
async_test_cls.py
"""
class Base:
async_driver = None
async 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()
「小贴士」:点击头像→【关注】按钮,获取更多软件测试的晋升认知不迷路! 🚀