VBScript 函数作为参数,或类似的构造
VBScript Function as Parameter, or similar Construct
我正在尝试在 HP Unified Functional Testing 中组合测试
程序员的方式。
对于那些不知道的人,该工具使用 VBScript 作为其驱动程序。
因为我想在多个 UFT 操作中使用同一个 DataTable 中的数据
-- 因为全局 table 已经有一组不同的数据
-- 我想从外部文件中检索数据。
UFT 很高兴支持此功能。
我目前的计划是 运行,这取决于我进行的测试,
我将仅遍历 table.
中的一系列行
这是我想出的脚本:
' targets the local sheet, but
' not the same value as dtLocalSheet
Const sheetNum = 2
dim sheetRowCount
DataTable.ImportSheet "PersonFile.xlsx", 1, sheetNum
sheetRowCount = DataTable.GetSheet(sheetNum).GetRowCount
dim firstRow, lastRow
firstRow = Parameter("FirstPersonIndex")
lastRow = Parameter("LastPersonIndex")
If sheetRowCount < lastRow Then
lastRow = sheetRowCount
End If
If sheetRowCount >= firstRow Then
Dim i
For i = firstRow To lastRow
DataTable.SetCurrentRow i
' begin payload
MsgBox(DataTable.Value("LastName", dtLocalSheet))
' end payload
Next
End if
我不想重复所有这些样板文件
每次我想使用这种模式。
我真的很想有这样的东西:
在函数库中:
sub LoopThroughSheetAnd(sheetFile, doThis)
' targets the local sheet, but
' not the same value as dtLocalSheet
Const sheetNum = 2
dim sheetRowCount
DataTable.ImportSheet sheetFile, 1, sheetNum
sheetRowCount = DataTable.GetSheet(sheetNum).GetRowCount
dim firstRow, lastRow
firstRow = Parameter("FirstRow")
lastRow = Parameter("LastRow")
If sheetRowCount < lastRow Then
lastRow = sheetRowCount
End If
If sheetRowCount >= firstRow Then
Dim i
For i = firstRow To lastRow
DataTable.SetCurrentRow i
call doThis()
Next
End if
end sub
原动作中...
sub Payload1()
MsgBox(DataTable.Value("LastName", dtLocalSheet))
end sub
LoopThroughSheetAnd "PersonFile.xlsx", Payload1
在一个单独的动作中,3 或 4 步之后...
sub Payload2()
' compare the data against another data source
end sub
LoopThroughSheetAnd "PersonFile.xlsx", Payload2
以上代码在 VBScript 中不起作用。
抛出类型不匹配错误
一旦我们尝试将 Payload1
作为参数传递。
如何在 VBScript 中合理地实现这一目标?
如果答案在 UFT 中也有效,则加分。
当我写下这个问题时,我的记忆开始运转,
然后我开始研究我曾经发现的 "feature"。
这在 HP UFT 的上下文中不起作用,
但是如果你是 运行 cscript,或者使用 Classic ASP,
你可以延迟声明一个函数,或者替换之前的声明,
改变它的工作方式。
VBScript 允许您声明相同的函数或子例程
在程序中多次。
它将最后的声明视为正确的声明。
你可以在 cscript 和 ASP 中通过物理分离来解决这个问题
函数的不同版本,
这样一个人就不会被另一个人破坏。
你必须小心不要将两者放在彼此靠近的任何地方,
或者你(你的继任者)可能有动脉瘤试图调试结果。
老实说,以其他方式重构代码可能会更好。
现在,免责声明已取消,
以下示例用于 cscript 或 wscript。
代码
因为这在 UFT 中无论如何都行不通,所以我将从头开始写。
在WrapperSub.vbs:
' Sub WrapperSub_Payload doesn't exist in this file.
' It must be declared by the calling file or the program will crash.
Sub WrapperSub()
wscript.echo("This begins the wrapper.")
WrapperSub_Payload
wscript.echo("This ends the wrapper.")
End Sub
在WrapperSubUseA.vbs:
With CreateObject("Scripting.FileSystemObject")
call ExecuteGlobal(.openTextFile("WrapperSub.vbs").readAll())
End With
Sub WrapperSub_Payload
wscript.echo("This is payload A.")
End Sub
WrapperSub
在WrapperSubUseB.vbs:
With CreateObject("Scripting.FileSystemObject")
call ExecuteGlobal(.openTextFile("WrapperSub.vbs").readAll())
End With
Sub WrapperSub_Payload
wscript.echo("This is payload B.")
End Sub
WrapperSub
输出
>cscript wrappersubusea.vbs
Microsoft (R) Windows Script Host Version 5.8
Copyright (C) Microsoft Corporation. All rights reserved.
This begins the wrapper.
This is payload A.
This ends the wrapper.
>cscript wrappersubuseb.vbs
Microsoft (R) Windows Script Host Version 5.8
Copyright (C) Microsoft Corporation. All rights reserved.
This begins the wrapper.
This is payload B.
This ends the wrapper.
请注意,如果 WrapperSub_Payload
的占位符
在源文件中声明,
该占位符将始终执行而不是预期的子例程。
这可能是由于 ExecuteGlobal
在解析当前文件后执行,
导致占位符在局部声明之后加载。
当您在 UFT 中尝试此操作时 --
将 WrapperSub.vbs 的内容放入函数库中 --
函数库理所当然地忽略了调用者的作用域。
然后它将失败,因为 WrapperSub_Payload
不存在于范围内。
您可以将函数作为参数传递给GetRef()
函数。这是一个实用程序 map
函数,就像您在 JavaScript 中找到的那样,它接受一个数组并为数组的每个元素调用一个函数:
Sub Map(a, f)
Dim i
For i = 0 To UBound(a)
' Call a function on each element and replace its value with the function return value
a(i) = f(a(i))
Next
End Sub
Map MyArray, GetRef("SomeFunc")
现在您可以编写 SomeFunc
以便它对值和 returns 更新值进行操作:
Function SomeFunc(i)
SomeFunc = i + 1
End Function
这很好用。 map
使用我们传递给它的函数 "pointer" 调用 SomeFunc
。
您可以使用 LoopThroughStreetAnd
函数执行类似的操作:
LoopThroughStreetAnd "PersonFile.xlsx", GetRef("Payload2")
VBScript 中的 标准 回调方式使用 GetRef, as in this demo.
使用对象时,可以将对方法的调用包装在对象中,然后传递对象。 (这在其他语言中已经差不多发生了,您只需在 VBScript 中手动完成即可。)
唯一的问题是任何以这种方式调用的方法都必须是 Public
。
我会使用 "Func1"、"Func2"、"Action1"、"Action2" 等命名方案,具体取决于函数的数量以及是否他们 return 值与否。
Dim s : Set s = New Something : s.Run
Class Something
Public Sub HowToPassMe(pValue)
WScript.Echo pValue
End Sub
Public Sub Run
Dim action : Set action = New Action1Wrapper
Set action.Target = Me
Dim se : Set se = New SomethingElse
se.DoSomethingElse action
End Sub
End Class
Class SomethingElse
Public Sub DoSomethingElse(pAction1)
pAction1.Action1("something")
End Sub
End Class
Class Action1Wrapper
Private mTarget
Public Property Set Target(value) : Set mTarget = value : End Property
Public Sub Action1(p1)
mTarget.HowToPassMe(p1)
End Sub
End Class
利用Execute
,Action1Wrapper
也可以写成类似下面的样子。也可以自己写一个工厂class方便使用
Class Action1Wrapper
Private mTarget
Public Property Set Target(value) : Set mTarget = value : End Property
Private mName
Public Property Let Name(value) : mName = value : End Property
Public Sub Action1(p1)
Execute "mTarget." & mName & "(p1)"
End Sub
End Class
Class Action1Factory_
Public Function Create(pTarget, pName)
Dim a1 : Set a1 = New Action1Wrapper
Set a1.Target = pTarget
a1.Name = pName
Set Create = a1
End Function
End Class
Dim Action1Factory : Set Action1Factory = New Action1Factory_
用作:
Dim action : Set action = Action1Factory.Create(Me, "HowToPassMe")
Dim se : Set se = New SomethingElse
se.DoSomethingElse action
我正在尝试在 HP Unified Functional Testing 中组合测试 程序员的方式。 对于那些不知道的人,该工具使用 VBScript 作为其驱动程序。
因为我想在多个 UFT 操作中使用同一个 DataTable 中的数据 -- 因为全局 table 已经有一组不同的数据 -- 我想从外部文件中检索数据。 UFT 很高兴支持此功能。
我目前的计划是 运行,这取决于我进行的测试, 我将仅遍历 table.
中的一系列行这是我想出的脚本:
' targets the local sheet, but
' not the same value as dtLocalSheet
Const sheetNum = 2
dim sheetRowCount
DataTable.ImportSheet "PersonFile.xlsx", 1, sheetNum
sheetRowCount = DataTable.GetSheet(sheetNum).GetRowCount
dim firstRow, lastRow
firstRow = Parameter("FirstPersonIndex")
lastRow = Parameter("LastPersonIndex")
If sheetRowCount < lastRow Then
lastRow = sheetRowCount
End If
If sheetRowCount >= firstRow Then
Dim i
For i = firstRow To lastRow
DataTable.SetCurrentRow i
' begin payload
MsgBox(DataTable.Value("LastName", dtLocalSheet))
' end payload
Next
End if
我不想重复所有这些样板文件 每次我想使用这种模式。 我真的很想有这样的东西:
在函数库中:
sub LoopThroughSheetAnd(sheetFile, doThis)
' targets the local sheet, but
' not the same value as dtLocalSheet
Const sheetNum = 2
dim sheetRowCount
DataTable.ImportSheet sheetFile, 1, sheetNum
sheetRowCount = DataTable.GetSheet(sheetNum).GetRowCount
dim firstRow, lastRow
firstRow = Parameter("FirstRow")
lastRow = Parameter("LastRow")
If sheetRowCount < lastRow Then
lastRow = sheetRowCount
End If
If sheetRowCount >= firstRow Then
Dim i
For i = firstRow To lastRow
DataTable.SetCurrentRow i
call doThis()
Next
End if
end sub
原动作中...
sub Payload1()
MsgBox(DataTable.Value("LastName", dtLocalSheet))
end sub
LoopThroughSheetAnd "PersonFile.xlsx", Payload1
在一个单独的动作中,3 或 4 步之后...
sub Payload2()
' compare the data against another data source
end sub
LoopThroughSheetAnd "PersonFile.xlsx", Payload2
以上代码在 VBScript 中不起作用。
抛出类型不匹配错误
一旦我们尝试将 Payload1
作为参数传递。
如何在 VBScript 中合理地实现这一目标? 如果答案在 UFT 中也有效,则加分。
当我写下这个问题时,我的记忆开始运转, 然后我开始研究我曾经发现的 "feature"。
这在 HP UFT 的上下文中不起作用, 但是如果你是 运行 cscript,或者使用 Classic ASP, 你可以延迟声明一个函数,或者替换之前的声明, 改变它的工作方式。
VBScript 允许您声明相同的函数或子例程 在程序中多次。 它将最后的声明视为正确的声明。
你可以在 cscript 和 ASP 中通过物理分离来解决这个问题 函数的不同版本, 这样一个人就不会被另一个人破坏。 你必须小心不要将两者放在彼此靠近的任何地方, 或者你(你的继任者)可能有动脉瘤试图调试结果。
老实说,以其他方式重构代码可能会更好。
现在,免责声明已取消, 以下示例用于 cscript 或 wscript。
代码
因为这在 UFT 中无论如何都行不通,所以我将从头开始写。
在WrapperSub.vbs:
' Sub WrapperSub_Payload doesn't exist in this file.
' It must be declared by the calling file or the program will crash.
Sub WrapperSub()
wscript.echo("This begins the wrapper.")
WrapperSub_Payload
wscript.echo("This ends the wrapper.")
End Sub
在WrapperSubUseA.vbs:
With CreateObject("Scripting.FileSystemObject")
call ExecuteGlobal(.openTextFile("WrapperSub.vbs").readAll())
End With
Sub WrapperSub_Payload
wscript.echo("This is payload A.")
End Sub
WrapperSub
在WrapperSubUseB.vbs:
With CreateObject("Scripting.FileSystemObject")
call ExecuteGlobal(.openTextFile("WrapperSub.vbs").readAll())
End With
Sub WrapperSub_Payload
wscript.echo("This is payload B.")
End Sub
WrapperSub
输出
>cscript wrappersubusea.vbs
Microsoft (R) Windows Script Host Version 5.8
Copyright (C) Microsoft Corporation. All rights reserved.
This begins the wrapper.
This is payload A.
This ends the wrapper.
>cscript wrappersubuseb.vbs
Microsoft (R) Windows Script Host Version 5.8
Copyright (C) Microsoft Corporation. All rights reserved.
This begins the wrapper.
This is payload B.
This ends the wrapper.
请注意,如果 WrapperSub_Payload
的占位符
在源文件中声明,
该占位符将始终执行而不是预期的子例程。
这可能是由于 ExecuteGlobal
在解析当前文件后执行,
导致占位符在局部声明之后加载。
当您在 UFT 中尝试此操作时 --
将 WrapperSub.vbs 的内容放入函数库中 --
函数库理所当然地忽略了调用者的作用域。
然后它将失败,因为 WrapperSub_Payload
不存在于范围内。
您可以将函数作为参数传递给GetRef()
函数。这是一个实用程序 map
函数,就像您在 JavaScript 中找到的那样,它接受一个数组并为数组的每个元素调用一个函数:
Sub Map(a, f)
Dim i
For i = 0 To UBound(a)
' Call a function on each element and replace its value with the function return value
a(i) = f(a(i))
Next
End Sub
Map MyArray, GetRef("SomeFunc")
现在您可以编写 SomeFunc
以便它对值和 returns 更新值进行操作:
Function SomeFunc(i)
SomeFunc = i + 1
End Function
这很好用。 map
使用我们传递给它的函数 "pointer" 调用 SomeFunc
。
您可以使用 LoopThroughStreetAnd
函数执行类似的操作:
LoopThroughStreetAnd "PersonFile.xlsx", GetRef("Payload2")
VBScript 中的 标准 回调方式使用 GetRef, as in this demo.
使用对象时,可以将对方法的调用包装在对象中,然后传递对象。 (这在其他语言中已经差不多发生了,您只需在 VBScript 中手动完成即可。)
唯一的问题是任何以这种方式调用的方法都必须是 Public
。
我会使用 "Func1"、"Func2"、"Action1"、"Action2" 等命名方案,具体取决于函数的数量以及是否他们 return 值与否。
Dim s : Set s = New Something : s.Run
Class Something
Public Sub HowToPassMe(pValue)
WScript.Echo pValue
End Sub
Public Sub Run
Dim action : Set action = New Action1Wrapper
Set action.Target = Me
Dim se : Set se = New SomethingElse
se.DoSomethingElse action
End Sub
End Class
Class SomethingElse
Public Sub DoSomethingElse(pAction1)
pAction1.Action1("something")
End Sub
End Class
Class Action1Wrapper
Private mTarget
Public Property Set Target(value) : Set mTarget = value : End Property
Public Sub Action1(p1)
mTarget.HowToPassMe(p1)
End Sub
End Class
利用Execute
,Action1Wrapper
也可以写成类似下面的样子。也可以自己写一个工厂class方便使用
Class Action1Wrapper
Private mTarget
Public Property Set Target(value) : Set mTarget = value : End Property
Private mName
Public Property Let Name(value) : mName = value : End Property
Public Sub Action1(p1)
Execute "mTarget." & mName & "(p1)"
End Sub
End Class
Class Action1Factory_
Public Function Create(pTarget, pName)
Dim a1 : Set a1 = New Action1Wrapper
Set a1.Target = pTarget
a1.Name = pName
Set Create = a1
End Function
End Class
Dim Action1Factory : Set Action1Factory = New Action1Factory_
用作:
Dim action : Set action = Action1Factory.Create(Me, "HowToPassMe")
Dim se : Set se = New SomethingElse
se.DoSomethingElse action