40. 自动化异步测试开发之编写异步业务函数、测试函数和测试类(类写法)

40. 自动化异步测试开发之编写异步业务函数、测试函数和测试类(类写法)

一、类结构设计解析

1.1 基类设计

python 复制代码
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 登录页面类

python 复制代码
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 主页面类

python 复制代码
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 登录测试类

python 复制代码
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'
  • 测试流程
    1. 打开登录页
    2. 执行登录
    3. 验证页面标题
  • 断言验证:检查标题是否为'Web Orders'

2.2 搜索测试类

python 复制代码
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()  # 退出登录
  • 测试流程
    1. 登录系统
    2. 执行搜索
    3. 验证错误提示
    4. 登出系统
  • 断言验证:检查是否显示街道字段不能为空的错误

三、完整测试执行示例

3.1 测试运行器

python 复制代码
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 预期执行结果

text 复制代码
执行登录测试...
登录测试通过 ✓
执行搜索测试...
搜索测试通过 ✓

3.3 实际操作流程

复制代码
登录测试:
  1. 打开登录页
  2. 输入用户名:Tester
  3. 输入密码:test
  4. 点击登录按钮
  5. 验证页面标题:Web Orders

搜索测试:
  1. 打开登录页
  2. 输入凭证登录
  3. 点击搜索菜单
  4. 点击搜索按钮(不输入内容)
  5. 验证错误提示:Field 'Street' cannot be empty.
  6. 点击登出链接

四、类写法的优势分析

4.1 继承结构

plaintext 复制代码
Base
  │
  ├── LoginPage
  │     │
  │     └── MainPage
  │           │
  │           ├── AsyncTestLogin
  │           │
  │           └── AsyncTestMain
  • 代码复用:通过继承复用公共方法和属性
  • 功能扩展:子类可以扩展或重写父类方法
  • 逻辑分层:清晰区分页面操作和测试验证

4.2 与函数写法的对比

特性 类写法 函数写法
状态管理 通过类属性维护状态 依赖参数传递状态
代码组织 按页面/功能模块组织 按操作流程组织
复用性 高(继承机制) 中(函数组合)
学习曲线 较陡峭(需理解OOP) 较平缓
适用场景 大型项目/复杂页面 小型项目/简单流程

五、最佳实践建议

5.1 类设计优化

python 复制代码
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 测试类优化

python 复制代码
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 执行入口优化

python 复制代码
async def main():
    driver = await create_driver()
    login_page = LoginPage(driver)
    await login_page.test_success_login()

这种基于类的异步测试开发模式,通过面向对象的设计思想,提供了更结构化、可维护性更高的测试代码组织方式,特别适合中大型自动化测试项目。

六、完整代码

python 复制代码
"""
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()

「小贴士」 :点击头像→【关注】按钮,获取更多软件测试的晋升认知不迷路! 🚀

相关推荐
站大爷IP6 分钟前
Python 办公实战:用 python-docx 自动生成 Word 文档
python
数通Dinner8 分钟前
异步Websocket构建聊天室
运维·网络·websocket·网络协议·信息与通信
MO2T32 分钟前
使用 Flask 构建基于 Dify 的企业资金投向与客户分类评估系统
后端·python·语言模型·flask
慢热型网友.35 分钟前
用 Docker 构建你的第一个 Python Flask 程序
python·docker·flask
Naiva35 分钟前
【小技巧】Python + PyCharm 小智AI配置MCP接入点使用说明(内测)( PyInstaller打包成 .exe 可执行文件)
开发语言·python·pycharm
云动雨颤39 分钟前
Python 自动化办公神器|一键转换所有文档为 PDF
运维·python
Fanmeang1 小时前
OSPF高级特性之FRR
运维·网络·华为·ip·ospf·spf·frr
小张不嚣张꒰ঌ(˚ᆺ˚)໒꒱1 小时前
Centos7在yum当中遇到Could not resolve host: mirrorlist.centos.org解决方案
linux·运维·服务器·centos
梅孔立1 小时前
yum update 报错 Cannot find a valid baseurl for repo: centos-sclo-rh/x86_64 等解决办法
linux·python·centos
Sca_杰1 小时前
centos指令
linux·运维·centos