我正在使用 RubberDuck 开发测试,并想测试程序的 MsgBox 输出。问题是程序在输出 MsgBox 后立即结束——字面上有一个“结束”语句。
运行 RubberDuck 测试并使用 Fakes.MsgBox.Returns 时,会出现不确定的黄色结果,并显示消息“运行测试时出现意外的 COM 异常”
我尝试在测试结束时放置“Assert.Fail”;然而,似乎节目的结尾把事情搞砸了。
是否可以在 RubberDuck 中进行测试以检测程序是否结束?
我正在使用 RubberDuck 开发测试,并想测试程序的 MsgBox 输出。问题是程序在输出 MsgBox 后立即结束——字面上有一个“结束”语句。
运行 RubberDuck 测试并使用 Fakes.MsgBox.Returns 时,会出现不确定的黄色结果,并显示消息“运行测试时出现意外的 COM 异常”
我尝试在测试结束时放置“Assert.Fail”;然而,似乎节目的结尾把事情搞砸了。
是否可以在 RubberDuck 中进行测试以检测程序是否结束?
Rubberduck 单元测试在 VBA 运行时的上下文中执行——也就是说,VBA 单元测试代码是从宿主应用程序内部运行的。测试结果通过其 API 报告回 Rubberduck。如果您查看插入测试模块时生成的 VBA 代码,它会基本了解测试运行的体系结构。以我们的集成测试套件中的这个单元测试为例:
'HERE BE DRAGONS. Save your work in ALL open windows.
'@TestModule
'@Folder("Tests")
Private Assert As New Rubberduck.AssertClass
Private Fakes As New Rubberduck.FakesProvider
'@TestMethod
Public Sub InputBoxFakeWorks()
On Error GoTo TestFail
Dim userInput As String
With Fakes.InputBox
.Returns vbNullString, 1
.ReturnsWhen "Prompt", "Second", "User entry 2", 2
userInput = InputBox("First")
Assert.IsTrue userInput = vbNullString
userInput = InputBox("Second")
Assert.IsTrue userInput = "User entry 2"
End With
TestExit:
Exit Sub
TestFail:
Assert.Fail "Test raised an error: #" & Err.Number & " - " & Err.Description
End Sub
这将创建一个托管类,该类“侦听”正在测试的代码中的断言,并评估通过或失败测试的条件。
Private Assert As New Rubberduck.AssertClass
这FakesProvider
是一个实用程序对象,用于将 VB 运行时中的挂钩设置为“忽略”或“欺骗”从 VB 运行时内部对InputBox
函数的调用。
由于Fakes
声明了对象As New
,因此该With
块FakesProvider
为测试实例化了 a。ThisInputBox
方法在 vbe7.dll 中的函数Fakes
上设置了一个挂钩,rtcInputBox
它将所有流量从 VBA 重定向到该函数到 Rubberduck 实现。这现在正在计算调用、跟踪传递的参数、提供返回值等。
With Fakes.InputBox
Returns 和 ReturnsWhen 调用使用 VBA 保存的 COM 对象将伪造调用的测试设置传递给InputBox
. 在此示例中,它将InputBox
对象配置为返回一个vbNullString
for call one,并在传递一个for call number 2的Prompt
参数时返回“用户条目 2”。"Second"
.Returns vbNullString, 1
.ReturnsWhen "Prompt", "Second", "User entry 2", 2
这就是 AssertClass 的用武之地。当您从 Rubberduck UI 运行单元测试时,它会确定用户代码的 COM 接口。然后,它通过该接口调用调用测试方法。然后 Rubberduck 使用AssertClass
来测试运行时条件。该IsTrue
方法将 aBoolean
作为参数(带有可选的输出消息)。因此,在下面的代码行中,VB 计算表达式userInput = vbNullString
并将结果作为参数传递给IsTrue
. 然后,RubberduckIsTrue
实现根据从 VBA 传递的参数是否满足所AssertClass
调用方法的条件来设置单元测试的状态。
Assert.IsTrue userInput = vbNullString
请注意,在上述代码执行方式的细分中,所有内容都在 VBA 环境中执行。Rubberduck 为 VBA 提供了一个“窗口”来通过AssertClass
对象报告结果,并且简单地(对于“简单”的某些值)通过FakesProvider
对象提供挂钩服务。VBA“拥有”这两个对象——它们只是通过 Rubberduck 的 COM 提供程序提供的。
当您End
在 VBA 中使用该语句时,它会在该点强制终止执行。Rubberduck COM 对象不再被客户端(您的测试过程)主动引用,并且不确定是否会减少 COM 对象上的引用计数。这就像从墙上拔出插头一样。Rubberduck 在这一点上唯一可以确定的是 COM 客户端已断开连接。在您的情况下,这表现为在 Rubberduck 中捕获的 COM 异常。由于 Rubberduck 无法知道它提供的对象为何失去通信,因此它将测试结果报告为“不确定”——它没有运行完成。
也就是说,解决方案是将代码重构为不使用End
. 曾经。引用上面链接的文档End
...
立即终止执行。本身从来不需要,但可以放置在过程中的任何位置以结束代码执行、关闭使用 Open 语句打开的文件以及清除变量。
这远非优雅,如果您引用了其他 COM 对象(除了 Rubberduck),则无法保证它们会可靠地终止。
全面披露,我为 Rubberduck 项目做出了贡献,并编写了一些上述代码。如果您想更好地了解单元测试的功能(并且可以阅读 c#),可以在此链接中找到COM 提供程序的实现。