41. 自动化测试开发之编写异步测试执行主函数
一、核心功能函数解析
1.1 异步测试函数执行器
async def async_func(func_list: List[FunctionType], *args, **kwargs):
async with ClientSession() as session:
# 启动浏览器
driver = await AsyncBrowser.start(*args, **kwargs, http_session=session)
async with driver as _driver:
# 顺序执行测试函数列表
for func in func_list:
await func(_driver)
- 功能:顺序执行一组测试函数
- 参数:
func_list
:测试函数列表*args, **kwargs
:传递给浏览器的参数
- 执行流程:
- 创建HTTP会话
- 启动浏览器实例
- 遍历执行每个测试函数
- 自动关闭浏览器
1.2 异步测试类执行器
async def async_cls(cls_list, *args, **kwargs):
async with ClientSession() as session:
# 启动浏览器
driver = await AsyncBrowser.start(*args, **kwargs, http_session=session)
async with driver as _driver:
# 遍历测试类
for cls in cls_list:
# 注入浏览器驱动
cls.async_driver = driver
instance = cls()
# 查找并执行测试方法
_attr_list = dir(instance)
for attr in _attr_list:
if 'test' in attr:
await eval(f'instance.{attr}()')
- 功能:执行一组测试类
- 特点:
- 自动查找类中所有包含"test"的方法
- 动态注入浏览器驱动实例
- 自动创建类实例
二、并发执行控制器
2.1 函数并发执行器
async def async_run_func(*args):
await asyncio.gather(*[async_func(*func_url_caps) for func_url_caps in args])
- 功能:并发执行多个测试任务组
- 参数结构:
(func_list, url, capabilities)
- 并发机制:使用
asyncio.gather
实现并行执行
2.2 类并发执行器
async def async_run_cls(*args):
await asyncio.gather(*[async_cls(*cls_url_caps) for cls_url_caps in args])
- 功能:并发执行多个测试类组
- 参数结构:
(cls_list, url, capabilities)
三、主入口函数
3.1 函数测试入口
def main_func(main_fields: List[Tuple[List, str, Dict]]):
"""
main_fields = [
([test_case1, test_case2], 'http://localhost:9515', {'browserName':'chrome'}),
([test_case3], 'http://remote-server:4444', {'browserName':'firefox'})
]
"""
asyncio.run(async_run_func(*main_fields))
- 参数格式:测试任务组列表
- 执行流程:启动事件循环执行并发任务
3.2 类测试入口
def main_cls(main_fields: List[Tuple[List, str, Dict]]):
"""
main_fields = [
([LoginTest, SearchTest], 'http://localhost:9515', {'browserName':'chrome'}),
([PerformanceTest], 'http://remote-server:4444', {'browserName':'firefox'})
]
"""
asyncio.run(async_run_cls(*main_fields))
- 适用场景:面向对象风格的测试代码
四、实战使用示例
4.1 定义测试函数
async def test_login(driver):
await driver.get('https://example.com/login')
await driver.send_keys('id', 'username', text='testuser')
await driver.send_keys('id', 'password', text='password123')
await driver.click('id', 'login-btn')
assert await driver.title() == 'Dashboard'
async def test_search(driver):
await driver.get('https://example.com/dashboard')
await driver.send_keys('id', 'search-box', text='product')
await driver.click('id', 'search-btn')
assert await driver.text('id', 'results-count') > 0
4.2 定义测试类
class LoginTest:
async_driver = None
async def test_valid_login(self):
await self.async_driver.get('https://example.com/login')
# ...登录操作
assert await self.async_driver.title() == 'Dashboard'
async def test_invalid_login(self):
await self.async_driver.get('https://example.com/login')
# ...错误凭证操作
assert 'Invalid credentials' in await self.async_driver.page_source()
4.3 执行函数测试
# 配置测试任务
test_config = [
(
[test_login, test_search], # 测试函数列表
'http://localhost:9515', # WebDriver地址
{'browserName': 'chrome'} # 浏览器能力
),
(
[test_search],
'http://remote-server:4444',
{'browserName': 'firefox'}
)
]
# 执行测试
main_func(test_config)
4.4 执行类测试
# 配置测试任务
test_config = [
(
[LoginTest], # 测试类列表
'http://localhost:9515',
{'browserName': 'chrome', 'headless': True}
),
(
[SearchTest, PerformanceTest],
'http://remote-server:4444',
{'browserName': 'firefox'}
)
]
# 执行测试
main_cls(test_config)
五、执行流程分析
5.1 函数测试执行流程
main_func启动
→ 创建事件循环
→ 调用async_run_func
→ 为每组配置创建async_func任务
→ 创建HTTP会话
→ 启动浏览器
→ 顺序执行组内测试函数
→ 关闭浏览器
→ 等待所有任务完成
5.2 并发执行效果
任务组 | 浏览器 | 测试数量 | 执行时间 |
---|---|---|---|
组1 | Chrome | 2个函数 | 8秒 |
组2 | Firefox | 1个函数 | 5秒 |
总耗时 | - | - | 8秒(并行执行) |
六、设计优势总结
-
灵活的任务组织:
- 支持函数式和面向对象两种风格
- 可自由组合测试用例
-
高效的并发执行:
- 利用asyncio.gather实现并行
- 减少总执行时间
-
资源自动管理:
- 自动创建/关闭HTTP会话
- 自动管理浏览器生命周期
-
配置驱动设计:
- 通过配置决定执行哪些测试
- 支持多浏览器/多环境测试
-
扩展性强:
- 易于添加新测试函数/类
- 支持分布式执行
这种主函数设计模式为大型自动化测试项目提供了清晰的任务组织和高效的执行机制,显著提升了测试执行效率。
七、完整代码
from aiohttp import ClientSession
from aiohttp.abc import AbstractStreamWriter
from chap9.async_webdriver import AsyncBrowser
from types import FunctionType
from typing import List, Tuple, Dict
import asyncio
async def async_func(func_list: List[FunctionType], *args, **kwargs):
async with ClientSession() as session:
driver = await AsyncBrowser.start(*args, **kwargs, http_session=session)
async with driver as _driver:
for func in func_list:
await func(_driver)
async def async_cls(cls_list, *args, **kwargs):
async with ClientSession() as session:
driver = await AsyncBrowser.start(*args, **kwargs, http_session=session)
async with driver as _driver:
for cls in cls_list:
cls.async_driver = driver
instance = cls()
_attr_list = dir(instance)
for attr in _attr_list:
if 'test' in attr:
await eval(f'instance.{attr}()')
async def async_run_func(*args):
await asyncio.gather(*[async_func(*func_url_caps) for func_url_caps in args])
async def async_run_cls(*args):
await asyncio.gather(*[async_cls(*cls_url_caps) for cls_url_caps in args])
# 执行,入口主函数
def main_func(main_fields: List[Tuple[List, AbstractStreamWriter, Dict]]):
"""
main_fields = [
([test_case, test_case1, ...], 'url', cap),
()
]
:param main_fields:
:return:
"""
asyncio.run(async_run_func(main_fields))
# 执行,入口主类
def main_cls(main_fields: List[Tuple[List, AbstractStreamWriter, Dict]]):
"""
main_fields = [
([test_cls1, test_cls2, ...], 'url', cap),
()
]
:param main_fields:
:return:
"""
asyncio.run(async_run_cls(main_fields))
「小贴士」:点击头像→【关注】按钮,获取更多软件测试的晋升认知不迷路! 🚀