影刀RPA完全指南:RPA流程系统测试规范与发布SOP完整手册
影刀RPA完全指南:RPA流程系统测试规范与发布SOP完整手册
作者:林焱
我之前做过一个淘宝订单采集的流程,在本机测试完全正常,发布给客户用的时候,第一天就报了5个不同的错——有元素找不到的、有Excel被占用的、有网络超时的。后来我总结了一套完整的测试和发布流程,现在每个流程发布前都要过一遍这个清单,基本能把90%的问题拦在发布前。
一、为什么需要系统测试?
1.1 RPA流程的特殊性
RPA流程和普通软件不一样,它依赖外部环境(浏览器、网站结构、文件路径、网络),所以测试时要考虑更多因素:
| 测试维度 | 普通软件 | RPA流程 |
|---|---|---|
| 运行环境 | 服务器环境可控 | 用户电脑环境各异 |
| 依赖项 | 依赖库版本固定 | 依赖浏览器版本、网站结构 |
| 数据输入 | 输入格式固定 | 输入数据可能格式不规范 |
| 异常处理 | 异常类型可预期 | 异常类型多(网络、元素、文件…) |
1.2 不测试直接发布的后果
- 流程运行失败,用户不知道怎么处理
- 数据错误(如重复写入、格式错误),事后修复成本高
- 用户信任度下降,不愿意用RPA
二、测试用例设计
2.1 测试用例的4个维度
1. 功能测试:流程能不能完成预期功能? 2. 边界值测试:输入数据在边界情况时,流程会不会崩? 3. 异常场景测试:遇到异常情况时,流程能不能 graceful 地处理? 4. 性能测试:流程运行速度能不能满足要求?2.2 功能测试用例模板
## 测试用例:淘宝订单采集 ### 用例1:正常流程 - 前置条件:Excel文件存在,淘宝已登录,网络正常 - 输入数据:店铺名称="店铺A",开始日期="2024-01-01",结束日期="2024-01-31" - 预期结果: 1. 流程成功运行完成 2. Excel文件里写入了订单数据 3. 订单数量 > 0 - 实际结果:(执行后填写) - 通过/不通过:(执行后填写) ### 用例2:Excel文件不存在 - 前置条件:Excel文件不存在 - 输入数据:同用例1 - 预期结果: 1. 流程捕获到"文件不存在"异常 2. 在日志里记录错误信息 3. 创建新的Excel文件 - 实际结果: - 通过/不通过: ### 用例3:淘宝未登录 - 前置条件:淘宝Cookie已过期 - 输入数据:同用例1 - 预期结果: 1. 流程检测到未登录状态 2. 弹出提示,要求用户手动登录 3. 用户登录后,流程继续执行 - 实际结果: - 通过/不通过:2.3 用Python自动化执行测试用例
店群矩阵自动化突破运营极限!
# -*- coding: utf-8 -*-# 功能:自动化执行RPA流程的测试用例importsubprocessimporttimeimportjsonclassRPATestRunner:""" RPA流程测试运行器 """def__init__(self,flow_path):""" :param flow_path: 影刀流程文件路径(.flow文件) """self.flow_path=flow_path self.test_results=[]defrun_test_case(self,case_name,input_data,expected_output):""" 运行单个测试用例 :param case_name: 测试用例名称 :param input_data: 输入数据(字典) :param expected_output: 预期输出(字典) :return: 测试结果(字典) """print(f'\n=== 运行测试用例:{case_name}===')start_time=time.time()try:# 用影刀命令行运行流程(假设影刀提供了命令行接口)# 实际使用时需要根据影刀的实际命令行参数来调整cmd=['yingdao','run','--flow',self.flow_path,'--input',json.dumps(input_data),'--output','test_output.json']# 运行流程result=subprocess.run(cmd,capture_output=True,text=True,timeout=300# 5分钟超时)elapsed_time=time.time()-start_timeifresult.returncode==0:# 流程运行成功,检查输出withopen('test_output.json','r',encoding='utf-8')asf:actual_output=json.load(f)# 对比预期输出和实际输出passed=self._compare_output(expected_output,actual_output)test_result={'case_name':case_name,'status':'PASS'ifpassedelse'FAIL','elapsed_time':elapsed_time,'input':input_data,'expected':expected_output,'actual':actual_output,'error':None}else:# 流程运行失败test_result={'case_name':case_name,'status':'ERROR','elapsed_time':elapsed_time,'input':input_data,'expected':expected_output,'actual':None,'error':result.stderr}exceptsubprocess.TimeoutExpired:# 流程运行超时test_result={'case_name':case_name,'status':'TIMEOUT','elapsed_time':300,'input':input_data,'expected':expected_output,'actual':None,'error':'流程运行超时(5分钟)'}exceptExceptionase:# 其他异常test_result={'case_name':case_name,'status':'ERROR','elapsed_time':time.time()-start_time,'input':input_data,'expected':expected_output,'actual':None,'error':str(e)}# 记录测试结果self.test_results.append(test_result)# 打印测试结果status_emoji='✅'iftest_result['status']=='PASS'else'❌'print(f'{status_emoji}测试结果:{test_result["status"]}({test_result["elapsed_time"]:.2f}秒)')iftest_result['error']:print(f' 错误:{test_result["error"]}')returntest_resultdef_compare_output(self,expected,actual):""" 对比预期输出和实际输出 """ifexpectedisNone:returnTrue# 没有预期输出,认为通过forkey,expected_valueinexpected.items():ifkeynotinactual:print(f' 缺少输出字段:{key}')returnFalseifactual[key]!=expected_value:print(f' 输出字段{key}不匹配:预期={expected_value},实际={actual[key]}')returnFalsereturnTruedefgenerate_test_report(self):""" 生成测试报告 """total=len(self.test_results)passed=sum(1forrinself.test_resultsifr['status']=='PASS')failed=sum(1forrinself.test_resultsifr['status']=='FAIL')errors=sum(1forrinself.test_resultsifr['status']in['ERROR','TIMEOUT'])report=f""" # RPA流程测试报告 ## 汇总 - 总测试用例数:{total}- 通过:{passed}- 失败:{failed}- 错误:{errors}- 通过率:{passed/total*100:.1f}% ## 详细结果 """fori,resultinenumerate(self.test_results,1):report+=f"\n### 用例{i}:{result['case_name']}\n"report+=f"- 状态:{result['status']}\n"report+=f"- 运行时间:{result['elapsed_time']:.2f}秒\n"ifresult['error']:report+=f"- 错误:{result['error']}\n"# 保存报告withopen('test_report.md','w',encoding='utf-8')asf:f.write(report)print(f'\n测试报告已生成:test_report.md')returnreport# 使用示例:# runner = RPATestRunner('C:/rpa/flows/taobao_order_collection.flow')## # 用例1:正常流程# runner.run_test_case(# case_name='正常流程',# input_data={'shop_name': '店铺A', 'start_date': '2024-01-01', 'end_date': '2024-01-31'},# expected_output={'status': 'success', 'order_count': 150}# )## # 用例2:Excel文件不存在# runner.run_test_case(# case_name='Excel文件不存在',# input_data={'shop_name': '店铺B', 'start_date': '2024-01-01', 'end_date': '2024-01-31'},# expected_output={'status': 'success'} # 预期会创建新文件# )## # 生成测试报告# runner.generate_test_report()三、边界值测试
3.1 什么是边界值?
边界值是指输入数据在「边界」情况时,流程是否还能正常运行。
举例:一个采集订单的流程,输入是日期范围。边界值包括:
- 开始日期 = 结束日期(只采集一天的数据)
- 日期范围是未来的日期(应该没有数据)
- 日期范围超过了平台允许的最大范围(如淘宝最多支持90天)
3.2 边界值测试用例
# -*- coding: utf-8 -*-# 功能:生成边界值测试用例defgenerate_boundary_test_cases():""" 生成边界值测试用例 """test_cases=[]# 边界值1:开始日期 = 结束日期test_cases.append({'case_name':'边界值:开始日期=结束日期','input':{'start_date':'2024-01-15','end_date':'2024-01-15'},'expected':{'status':'success','order_count':0# 假设这一天没有订单}})# 边界值2:日期范围是未来test_cases.append({'case_name':'边界值:日期范围是未来','input':{'start_date':'2025-01-01','end_date':'2025-01-31'},'expected':{'status':'success','order_count':0}})# 边界值3:日期范围超过90天(淘宝限制)test_cases.append({'case_name':'边界值:日期范围超过90天','input':{'start_date':'2024-01-01','end_date':'2024-05-01'# 121天},'expected':{'status':'error',# 预期会报错'error_message':'日期范围不能超过90天'}})# 边界值4:空输入test_cases.append({'case_name':'边界值:空输入','input':{'start_date':'','end_date':''},'expected':{'status':'error','error_message':'日期不能为空'}})returntest_cases四、异常场景测试
4.1 常见的异常场景
| 异常类型 | 场景 | 预期处理方式 |
|---|---|---|
| 网络异常 | 网络断开、请求超时 | 重试3次,还失败则记录错误并继续 |
| 元素找不到 | 网站改版,元素定位失效 | 截图+记录错误,跳过当前任务 |
| 文件被占用 | Excel文件被手动打开 | 等待3秒后重试,最多重试3次 |
| 数据格式异常 | 某个字段的值是None或格式不对 | 用默认值替代,记录警告日志 |
| Cookie失效 | 登录态过期 | 检测失效,提示用户重新登录 |
4.2 模拟异常场景进行测试
# -*- coding: utf-8 -*-# 功能:模拟异常场景,测试流程的异常处理能力importrandomdefsimulate_network_exception(probability=0.3):""" 模拟网络异常(用于测试) :param probability: 异常概率(0-1) """ifrandom.random()<probability:raiseException('模拟网络异常:Connection timeout')defsimulate_element_not_found(probability=0.2):""" 模拟元素找不到异常 """ifrandom.random()<probability:raiseException('模拟元素找不到:NoSuchElementException')deftest_exception_handling(flow_func):""" 测试流程的异常处理能力 :param flow_func: 流程函数 """print('\n=== 异常场景测试 ===')# 测试1:网络异常print('\n测试1:网络异常')try:simulate_network_exception(probability=1.0)# 100%触发异常flow_func()print('❌ 流程没有捕获网络异常')exceptExceptionase:print(f'✅ 流程捕获到异常:{str(e)}')# 检查是否有重试逻辑# (需要在流程里加日志记录重试次数)# 测试2:元素找不到print('\n测试2:元素找不到')try:simulate_element_not_found(probability=1.0)flow_func()print('❌ 流程没有捕获元素找不到异常')exceptExceptionase:print(f'✅ 流程捕获到异常:{str(e)}')五、性能测试
5.1 性能测试的指标
| 指标 | 说明 | 目标值 |
|---|---|---|
| 单次运行时间 | 流程运行一次需要多长时间 | 根据任务复杂度定(如采集100条数据<5分钟) |
| 内存占用 | 流程运行时占用多少内存 | < 1GB(避免影响其他程序) |
| CPU占用 | 流程运行时占用多少CPU | < 50%(避免卡顿) |
| 吞吐量 | 单位时间内能处理多少数据 | 根据业务需求定 |
5.2 性能测试代码
# -*- coding: utf-8 -*-# 功能:RPA流程性能测试importtimeimportpsutilimportosdefmeasure_performance(flow_func,*args,**kwargs):""" 测量流程的性能指标 :param flow_func: 流程函数 :return: 性能测试结果 """print('\n=== 性能测试 ===')# 记录开始时间start_time=time.time()# 记录开始时的内存占用process=psutil.Process(os.getpid())start_memory=process.memory_info().rss/1024/1024# MBtry:# 运行流程result=flow_func(*args,**kwargs)# 记录结束时间end_time=time.time()# 记录结束时的内存占用end_memory=process.memory_info().rss/1024/1024# MB# 计算性能指标elapsed_time=end_time-start_time memory_used=end_memory-start_memoryprint(f'运行时间:{elapsed_time:.2f}秒')print(f'内存占用:{memory_used:.2f}MB')print(f'处理结果:{result}')return{'elapsed_time':elapsed_time,'memory_used':memory_used,'result':result}exceptExceptionase:print(f'性能测试失败:{str(e)}')returnNone# 使用示例:# def my_flow():# # 你的流程逻辑# time.sleep(5) # 模拟流程运行# return {'status': 'success'}## performance = measure_performance(my_flow)六、发布前检查清单
6.1 代码质量检查
□ 所有变量都有有意义的名称(不用 var1, temp 这种) □ 所有关键步骤都有注释 □ 所有异常都有捕获和处理 □ 没有硬编码的密码、Cookie(用配置文件或变量) □ 文件路径用的是相对路径或配置变量(不用绝对路径) □ 所有的「等待」指令都有合理的超时时间 □ 所有的「循环」都有退出条件(不会无限循环)6.2 运行环境检查
□ 流程在目标机器上能正常运行(不光是开发机器) □ 目标机器上安装了需要的浏览器(Chrome/Edge) □ 目标机器上的浏览器版本和开发机器一致(或兼容) □ 目标机器能访问需要的网站(网络通畅) □ 目标机器上有足够的磁盘空间(用于保存日志和数据)6.3 文档检查
□ 有流程说明文档(做什么、输入什么、输出什么) □ 有使用手册(截图+步骤说明) □ 有常见问题文档(用户可能遇到的问题) □ 有版本更新日志(每次发布记录了改了什么)七、版本管理
7.1 为什么需要版本管理?
RPA流程在使用过程中,经常会遇到需要修改的情况:
temu店群自动化报活动案例
- 网站改版了,元素定位要更新
- 业务需求变了,流程逻辑要调整
- 发现了bug,要修复
如果没有版本管理,改着改着就不知道哪个是最新版本了。
7.2 版本命名规范
格式:主版本号.次版本号.修订号 示例:v1.2.3 主版本号:有大改动(如架构重构、主要逻辑变更)  次版本号:有新功能增加 修订号:bug修复、小调整7.3 用Git做版本管理
# 初始化Git仓库cdC:/rpa/flowsgitinit# 添加文件到仓库gitadd.# 提交(写清楚的提交信息)gitcommit-m"v1.0.0: 初始版本,支持淘宝订单采集"# 打标签(标记版本)gittag-av1.0.0-m"第一个正式版本"# 查看历史版本gitlog--oneline# 回滚到某个版本gitcheckout v1.0.07.4 版本发布SOP
1. 【代码冻结】-> 发布前3天,不再加新功能,只修bug 2. 【完整测试】-> 过一遍测试清单,所有测试用例都通过 3. 【更新文档】-> 更新使用手册、常见问题、版本日志 4. 【打包发布】-> 导出流程文件(.flow),打包成zip 5. 【通知用户】-> 发送飞书通知,告知新版本和改动点 6. 【监控运行】-> 发布后3天内,密切监控运行情况常见问题速查
| # | 现象 | 原因 | 解决方式 |
|---|---|---|---|
| 1 | 流程在本机正常,到用户机器上报错 | 环境不一致(浏览器版本、文件路径) | 用相对路径,在用户机器上完整测试一遍 |
| 2 | 测试用例运行失败,但手动运行正常 | 测试环境和手动运行环境不一致 | 检查测试环境的配置(如Cookie、文件路径) |
| 3 | 性能测试时内存占用很高 | 变量没有释放,或数据量太大 | 在循环体内及时清空大变量,或分批处理数据 |
| 4 | 版本回滚后,流程还是报错 | 回滚不彻底(如配置文件没回滚) | 用Git管理所有文件(包括配置文件),确保回滚完整 |
| 5 | 用户不知道怎么用新版本 | 缺少使用手册或更新说明 | 每次发布都写清楚的更新日志,并通知用户 |
| 6 | 发布后发现bug,但旧版本找不到了 | 没有做好版本归档 | 用Git的tag功能标记每个发布版本,并定期备份 |
| 7 | 流程运行速度越来越慢 | 日志文件太大,或数据文件没有清理 | 定期清理日志(保留最近30天),数据文件定期归档 |
推荐资源
- 影刀RPA官方文档:https://academy.yingdao.com/
- Git版本管理教程:https://git-scm.com/book/zh/v2
- 软件测试基础知识:https://www.51testing.com/
内容标签
影刀RPA流程测试版本管理发布SOP测试用例边界值测试异常测试性能测试质量控制最佳实践
转载请注明出处:飞行社社区,作者:林焱
