4

我正在使用 RubberDuck 开发测试,并想测试程序的 MsgBox 输出。问题是程序在输出 MsgBox 后立即结束——字面上有一个“结束”语句。

运行 RubberDuck 测试并使用 Fakes.MsgBox.Returns 时,会出现不确定的黄色结果,并显示消息“运行测试时出现意外的 COM 异常”

我尝试在测试结束时放置“Assert.Fail”;然而,似乎节目的结尾把事情搞砸了。

是否可以在 RubberDuck 中进行测试以检测程序是否结束?

4

1 回答 1

6

tldr; 不

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,因此该WithFakesProvider为测试实例化了 a。ThisInputBox方法在 vbe7.dll 中的函数Fakes上设置了一个挂钩,rtcInputBox它将所有流量从 VBA 重定向到该函数到 Rubberduck 实现。这现在正在计算调用、跟踪传递的参数、提供返回值等。

    With Fakes.InputBox

Returns 和 ReturnsWhen 调用使用 VBA 保存的 COM 对象将伪造调用的测试设置传递给InputBox. 在此示例中,它将InputBox对象配置为返回一个vbNullStringfor 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 提供程序的实现。

于 2018-12-26T00:04:46.857 回答