《软件自动化测试成功之道》节选11 - 常用脚本函数的封装

本文介绍了自动化测试中常用的字符串操作函数封装方法,包括替换、分割、读取、查找、匹配开头和结尾等操作,并提供了具体的实现代码及测试示例。

《软件自动化测试成功之道》节选11 - 常用脚本函数的封装

http://www.china-pub.com/196509

 

 

 

 

项目回顾>>>

2008-5-29

今天发现很多地方需要使用到字符串的操作,而DelphiScipt的字符串操作函数不大好用,因此有必要把常用的一些字符串操作封装成函数,已备调用。

 

1 替换字符串

//.............................................................................

// 目的:替换字符串

// 输入参数:

//          Str:待替换的字符串

//          StrTobeRep:需要被替换掉的字符串

//          StrRep:替换的字符串

// 返回结果:替换后的字符串

// 注意事项:

// 作者:陈能技

// 日期:2008-5-29

//.............................................................................

Function String_RePlace(Str,StrTobeRep,StrRep);

begin

  Str := dotNET.System_Text.StringBuilder.zctor(Str);

  Str := Str.RePlace_2(StrTobeRep,StrRep).ToString;

  Result := Str;

end;

Procedure Test_String_RePlace;

  Var Str : OleVariant;

begin

  Str := String_RePlace('D:/Code/FileSelctionDialog/FileSelctionDialog/bin/Debug','D:','|桌面|我的电脑|本地磁盘 (D:)');

  Log.Message(String_RePlace(Str,'/','|'));

end;

这里使用的是.NET类库中的StringBuilder对象,另外,也可以使用Utilities对象中的StringReplace来替换字符串:

Procedure Test_String_RePlace_2;

  Var Str : OleVariant;

begin

  Str := Utilities.StringReplace('D:/Code/FileSelctionDialog/FileSelctionDialog/bin/Debug','D:','|桌面|我的电脑|本地磁盘 (D:)',1);

  Log.Message(Utilities.StringReplace(Str,'/','|',1));

end;

其中StringReplace的第三个参数是一个标志位,可以使用以下两个值:

1:是否替换所有。如果没有使用该值,则仅替换找到的第一个。

2:是否大小写敏感。使用该值表示大小写不敏感地匹配,不使用该值则表示大小写敏感。

2)分割字符串

//.............................................................................

// 目的:分割字符串

// 输入参数:

//          Str:待替换的字符串

//          Separator:分割符

// 返回结果:分割后的字符串保存在数组并返回

// 注意事项:

// 作者:陈能技

// 日期:2008-5-29

//.............................................................................

Function String_Split(Str,Separator);

  Var StringArray,Count ,I: OleVariant;

begin

  StringArray := CreateVariantArray(0,0);

  Count := BuiltIn.GetListCount(Str, Separator);

  VarArrayRedim(StringArray,Count);

  For I:=0 to Count - 1 do

  begin

    StringArray[I] := BuiltIn.GetListItem(Str , I , Separator);

  end;

  Result := StringArray;

end; 

Procedure Test_String_Split;

  Var StringArray,I : OleVariant;

begin

  StringArray := String_Split('D:/Code/FileSelctionDialog/FileSelctionDialog/bin/Debug','/');

  For I := 0 to VarArrayHighBound(StringArray,1)-1 do

  begin

    Log.Message(StringArray[I]);

  end;

end;

3)读取部分字符串

//.............................................................................

// 目的:读取字符串中的一部分

// 输入参数:

//          Str:待读取的字符串

//          StartIndex:开始读取的Index位置

//          EndIndex:结束读取的Index位置

// 返回结果:读取到的部分字符串

// 注意事项:

// 作者:陈能技

// 日期:2008-5-29

//.............................................................................

Function String_SubString(Str,StartIndex,EndIndex);

begin

  Str := dotNET.System_Text.StringBuilder.zctor(Str).ToString;

  Str := Str.SubString(StartIndex,EndIndex).ToString;

  Result := Str;   

end;

Procedure Test_String_SubString;

begin

  Log.Message(String_SubString('1234567890',0,2));

end;

这里使用的是.NET类库中的StringBuilder对象的SubString函数,另外,也可以使用使用DelphiScriptCopy函数来读取字符串中的一部分:

// 使用DelphiScriptCopy函数来读取字符串中的一部分

Procedure Test_String_SubString_2;

  var Str : OleVariant;

begin

  Str:='1234567890'; 

  Log.Message(Copy(Str,2,5)); 

  Log.Message(Copy(Str,2, 20));

  Log.Message(Copy(Str,-2,3));

  Log.Message(Copy(Str,2,0));

end;

4)查找字符串

查找字符串可以使用.NETString对象的Contains函数,用于确定是否包含指定的字符串:

//.............................................................................

// 目的:查找字符串是否包含指定的字符串

// 输入参数:

//          Str:待查找的字符串

//          SubStr:被查找的字符串

// 返回结果:

//          True:包含指定的字符串

//          False:不包含指定的字符串

// 注意事项:

// 作者:陈能技

// 日期:2008-5-29

//.............................................................................

Function String_Contains(Str,SubStr);

begin

  Str := dotNET.System_Text.StringBuilder.zctor(Str).ToString;

  Result := Str.Contains(SubStr);

end;

Procedure Test_String_Contains;

begin

   Log.Message(String_Contains('01234567890','456'));   

end;

另外,也可以使用DelphiScript提供的Pos函数来判断是否出现指定的字符串,如果包含,则返回第一个出现的位置;如果不包含,则返回False值,如下代码所示:

//.............................................................................

// 目的:查找字符串是否包含指定的字符串

// 输入参数:

//          Str:待查找的字符串

//          SubStr:被查找的字符串

// 返回结果:

// 注意事项:

// 作者:陈能技

// 日期:2008-5-29

//.............................................................................

Function String_Contains_2(Str,SubStr);

  Var Index :OleVariant;

begin

  Result := False;

  Index := Pos(SubStr,Str);

  IF Index > 0 then

    Result := Index;

end;

Procedure Test_String_Contains_2;

begin

   Log.Message(String_Contains_2('01234567890','456'));   

end;

5)匹配开头字符串

//.............................................................................

// 目的:判断字符串是否以指定的字符串开头

// 输入参数:

//          Str:待查找的字符串

//          SubStr:开头的字符串

// 返回结果:

//          True:字符串以指定的字符串开头

//          False:字符串不以指定的字符串开头

// 注意事项:

// 作者:陈能技

// 日期:2008-5-29

//.............................................................................

Function String_StartsWith(Str,SubStr);

begin

  Str := dotNET.System_Text.StringBuilder.zctor(Str).ToString;

  Result := Str.StartsWith(SubStr);

end;

Procedure Test_String_StartsWith;

begin

   Log.Message(String_StartsWith('01234567890','123'));   

end;

6)匹配结尾字符串

//.............................................................................

// 目的:判断字符串是否以指定的字符串结尾

// 输入参数:

//          Str:待查找的字符串

//          SubStr:结尾的字符串

// 返回结果:

//          True:字符串以指定的字符串结尾

//          False:字符串不以指定的字符串结尾

// 注意事项:

// 作者:陈能技

// 日期:2008-5-29

//.............................................................................

Function String_EndsWith(Str,SubStr);

begin

  Str := dotNET.System_Text.StringBuilder.zctor(Str).ToString;

  SubStr := dotNET.System_Text.StringBuilder.zctor(SubStr).ToString; 

  Result := Str.EndsWith(SubStr);

end;

Procedure Test_String_EndsWith;

begin

   Log.Message(String_EndsWith('01234567890','1230'));   

end;

《软件自动化测试成功之道》节选11 - 常用脚本函数的封装

http://www.china-pub.com/196509

非常好的线索:**手动操作 SAP 快,但通过 VBA 脚本控制时变慢**。 这说明: - ✅ SAP 系统本身正常 - ✅ 网络连接良好 - ❌ 问题出在 **VBA 与 SAP GUI 的交互过程**中 --- ## 🔍 根本原因分析:为什么“手动快、脚本慢”? ### 📌 主要原因如下: | 原因 | 解释 | |------|------| | 1. **SAP GUI 刷新阻塞(焦点丢失)** | Excel 执行 VBA 时会抢占前台窗口焦点,导致 SAP 处于后台,SAP GUI 暂停渲染和消息处理 | | 2. **未正确激活 SAP 窗口** | `.AppActivate` 失效或时机不对,SAP 不响应输入 | | 3. **缺少 `DoEvents` 导致 Windows 消息队列堵塞** | 长时间运行的 VBA 不释放 CPU,系统无法调度 SAP 接收指令 | | 4. **`.FindById()` 查找失败后等待超时(60秒默认)** | 如果控件 ID 错误或尚未加载,VBA 会卡住直到超时 | | 5. **SAP 脚本引擎响应延迟(SendCommand 缓冲区积压)** | 连续发送多个 `.Press` 或 `.Text` 没有适当延时,SAP 来不及处理 | --- ## ✅ 解决方案 & 优化代码建议 以下是你原代码中的关键点增强版本,专治“手动快、脚本慢”的问题。 ### ✅ 1. 强制激活 SAP 窗口 + 添加 DoEvents + 延时控制 ```vba ' === 修改你的 STEP 2 后面添加这段 === On Error Resume Next Dim sapTitle As String sapTitle = Session.Info.SystemName & " - " & Session.Info.Client AppActivate sapTitle Application.Wait Now + TimeSerial(0, 0, 1) ' 等待1秒让窗口激活 DoEvents ' 释放消息队列 On Error GoTo ErrHandler ``` > 💡 即使你写了 `AppActivate`,也必须配合 `DoEvents` 和延迟才能生效! --- ### ✅ 2. 封装安全的 `.FindById` 调用函数(带超时和重试) 很多卡顿是因为 `.FindById("xxx")` 找不到对象而无限等待。我们加一个容错封装: ```vba Function SafeGetElement(session As Object, elementId As String, timeoutSeconds As Long) As Object Dim startTime As Double Set SafeGetElement = Nothing startTime = Timer Do While Timer - startTime < timeoutSeconds On Error Resume Next Set SafeGetElement = session.FindById(elementId, False) ' 第二个参数 False 表示不抛异常 On Error GoTo 0 If Not SafeGetElement Is Nothing Then Exit Do Application.Wait Now + TimeSerial(0, 0, 1) DoEvents Loop End Function ``` #### 使用方式替换原来的 `.FindById`: ```vba With Session .StartTransaction "MB51" Dim btn As Object Set btn = SafeGetElement(Session, "wnd[0]/tbar[1]/btn[17]", 10) If Not btn Is Nothing Then btn.Press Else MsgBox "按钮未找到: wnd[0]/tbar[1]/btn[17]" Exit Sub End If End With ``` --- ### ✅ 3. 在每一步 `.Press` 或 `.DoubleClickCurrentCell` 后强制刷新 UI ```vba .Session.FindById("wnd[0]/tbar[1]/btn[8]").Press ' 执行查询 ' --- 关键:等待 ALV 加载完成 --- Dim alvShell As Object Set alvShell = SafeGetElement(Session, "wnd[0]/usr/cntlGRID1/shellcont/shell", 30) If alvShell Is Nothing Then MsgBox "ALV 表格未加载成功" Exit Sub End If AppActivate Session.Info.SystemName & " - " & Session.Info.Client DoEvents Application.Wait Now + TimeSerial(0, 0, 2) ' 给 ALV 渲染时间 ``` --- ### ✅ 4. 减少不必要的 `.Children` 层级判断(简化连接逻辑) 你可以直接尝试获取第一个可用会话,不必层层嵌套判断: ```vba Set Session = Nothing On Error Resume Next Set Session = SapGui.GetScriptingEngine.Children(0).Children(0) On Error GoTo ErrHandler If Session Is Nothing Then MsgBox "无法获取 SAP 会话,请确保已登录 Z2L 系统。", vbCritical Exit Sub End If ``` > ⚠️ 注意:前提是用户已经打开并登录了 Z2L。 --- ### ✅ 5. 关闭 SAP 自动提示弹窗(防止阻塞) 有些系统会在登录后弹出公告、消息框等,可以用以下方式关闭: ```vba ' === 新增通用函数:关闭所有弹窗 === Sub CloseAllPopups(session As Object) On Error Resume Next Do While session.Children.Count > 1 session.FindById("wnd[1]/tbar[0]/btn[3]").Press ' 按取消 Application.Wait Now + TimeSerial(0, 0, 1) DoEvents Loop On Error GoTo 0 End Sub ``` 在开始前调用一次: ```vba Call CloseAllPopups(Session) ``` --- ### ✅ 6. 使用 `/n` 命令清空事务状态(避免残留影响) ```vba Session.SendCommand "/nMB51" ' 比 StartTransaction 更快更干净 ``` 替代: ```vba .Session.StartTransaction "MB51" ``` 因为 `StartTransaction` 可能还要处理旧状态,而 `/nXXX` 是强制新开。 --- ## ✅ 最终优化建议总结 | 优化项 | 是否推荐 | |-------|--------| | ✅ 添加 `AppActivate + DoEvents + Wait` | ✔️ 必须 | | ✅ 使用 `SafeGetElement` 替代 `.FindById` | ✔️ 强烈推荐 | | ✅ 每步操作后 `DoEvents` 和短等待 | ✔️ 推荐 | | ✅ 改用 `SendCommand "/nMB51"` | ✔️ 更稳定 | | ✅ 关闭所有弹窗(CloseAllPopups) | ✔️ 安全起见 | | ✅ 避免使用 OneDrive/Desktop 路径导出 | ✔️ 改用 `%TEMP%` | --- ### 示例:优化后的核心执行部分(节选) ```vba With Session Call CloseAllPopups(Session) .SendCommand "/nMB51" AppActivate .Info.SystemName & " - " & .Info.Client DoEvents: Application.Wait Now + TimeSerial(0, 0, 1) Dim elem As Object Set elem = SafeGetElement(Session, "wnd[0]/tbar[1]/btn[17]", 10) If Not elem Is Nothing Then elem.Press Else GoTo ErrHandler Set elem = SafeGetElement(Session, "wnd[1]/tbar[0]/btn[6]", 10) If Not elem Is Nothing Then elem.Press Else GoTo ErrHandler ' ... 其他操作类似处理 End With ``` --- ## ✅ 如何验证是否解决? 1. 在任务管理器中观察: - SAP GUI 进程是否始终处于“Running”状态? - 是否有频繁的暂停/恢复? 2. 在代码中加入日志输出: ```vba Debug.Print "Step 1: Opened MB51 - " & Now() ``` 查看各步骤耗时分布。 --- ###
评论 12
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值