RubberDuck 可以测试程序是否结束吗?

Can RubberDuck test if a program ends?

我正在使用 RubberDuck 开发测试,并想测试程序的 MsgBox 输出。问题是程序在输出 MsgBox 后立即结束 - 确实有一个 "End" 语句。

当 运行 RubberDuck 测试并使用 Fakes.MsgBox.Returns 时,有一个不确定的黄色结果和消息 "Unexpected COM exception while running tests"

我试过在测试结束时放置一个 "Assert.Fail";但是,似乎程序结束时会把事情搞砸。

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

tldr;否

Rubberduck 单元测试在 VBA 运行 时间的上下文中执行 - 也就是说,VBA 单元测试代码正在从内部 运行主机应用程序。测试结果通过 API 报告给 Rubberduck。如果您查看插入测试模块时生成的 VB 代码,它给出了测试架构的基本概念 运行。以我们的集成测试套件中的这个单元测试为例:

'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

细分:

这会创建一个托管 class,"listens" 用于正在测试的代码中的断言,并评估通过或未通过测试的条件。

Private Assert As New Rubberduck.AssertClass     

FakesProvider 是一个实用对象,用于在 VB 运行 时间设置挂钩到 "ignore" 或 "spoof" 从 VB 运行是时候,比方说,InputBox 函数了。

由于 Fakes 对象被声明为 As NewWith 块为测试实例化了一个 FakesProviderFakesInputBox 方法这会在 vbe7.dll 中的 rtcInputBox 函数上设置一个挂钩,它将所有流量从 VBA 重定向到该函数的 Rubberduck 实现。这现在正在计算调用、跟踪传递的参数、提供 return 值等

    With Fakes.InputBox

Returns 和 ReturnsWhen 调用使用 VB 持有的 COM 对象将伪造调用的测试设置传达给 InputBox。在此示例中,它将 InputBox 对象配置为 return 一个 vbNullString 用于调用 1,并且 "User entry 2" 当传递 Prompt 参数时 "Second"电话号码二。

        .Returns vbNullString, 1
        .ReturnsWhen "Prompt", "Second", "User entry 2", 2

这就是 AssertClass 发挥作用的地方。当您 运行 从 Rubberduck UI 进行单元测试时,它会确定用户代码的 COM 接口。然后,它调用通过该接口调用测试方法。 Rubberduck 然后使用 AssertClass 来测试 运行 时间条件。 IsTrue 方法将 Boolean 作为参数(带有可选的输出消息)。因此在下面的代码行中,VB 计算表达式 userInput = vbNullString 并将结果作为参数传递给 IsTrue。 Rubberduck IsTrue 实现然后根据从 VBA 传递的参数是否满足调用的 AssertClass 方法的条件来设置单元测试的状态。

        Assert.IsTrue userInput = vbNullString

这对您的问题意味着什么:

请注意,在上述代码执行方式的分解中,一切都在 VBA 环境 中执行。 Rubberduck 正在提供 VBA a "window" 以通过 AssertClass 对象报告结果,并且简单地(对于 "simply" 的某些值)通过 [=15 提供挂钩服务=] 对象。 VBA "owns" 这两个对象 - 它们只是通过 Rubberduck 的 COM 提供程序提供的。

当您在 VBA 中使用 End 语句时,它会在该点强制终止执行。 Rubberduck COM 对象不再被客户端(您的测试过程)主动引用,并且不确定是否会减少 COM 对象的引用计数。这就像从墙上拔下插头一样。此时 Rubberduck 唯一可以确定的是 COM 客户端已断开连接。在您的情况下,表现为在 Rubberduck 中捕获的 COM 异常。由于 Rubberduck 无法知道 为什么 它提供的对象失去了通信,它将测试结果报告为 "Inconclusive" - 它没有 运行 完成.


也就是说,解决方案是重构您的代码以不使用 End。曾经。引用上面链接的文档 End...

Terminates execution immediately. Never required by itself but may be placed anywhere in a procedure to end code execution, close files opened with the Open statement, and to clear variables.

这远不是优雅的,如果您引用了其他 COM 对象(Rubberduck 除外),则无法保证它们会可靠地终止。


完全公开,我为 Rubberduck 项目做出了贡献并编写了上面描述的一些代码。如果您想更好地了解单元测试的功能(并且可以阅读 c#),COM 提供程序的实现 can be found at this link.