简体   繁体   English

测试MS Access应用程序的最佳方法?

[英]Best way to test a MS Access application?

With the code, forms and data inside the same database I am wondering what are the best practices to design a suite of tests for a Microsoft Access application (say for Access 2007). 使用同一数据库中的代码,表单和数据,我想知道为Microsoft Access应用程序设计一套测试的最佳实践是什么(比如Access 2007)。

One of the main issues with testing forms is that only a few controls have a hwnd handle and other controls only get one they have focus, which makes automation quite opaque since you cant get a list of controls on a form to act on. 测试表单的主要问题之一是只有少数控件具有hwnd句柄,而其他控件只能获得一个焦点,这使得自动化非常不透明,因为您无法获取表单上的控件列表来执行操作。

Any experience to share? 有经验可以分享吗?

1. Write Testable Code 1.编写可测试代码

First, stop writing business logic into your Form's code behind. 首先,停止将业务逻辑写入您的Form的代码中。 That's not the place for it. 那不是它的地方。 It can't be properly tested there. 它无法在那里进行适当的测试。 In fact, you really shouldn't have to test your form itself at all. 事实上,你真的不应该自己测试你的表单。 It should be a dead dumb simple view that responds to User Interaction and then delegates responsibility for responding to those actions to another class that is testable. 这应该是响应用户交互,然后响应这些行动,另一类可测试委托责任死哑巴简单视图。

How do you do that? 你是怎样做的? Familiarizing yourself with the Model-View-Controller pattern is a good start. 熟悉模型 - 视图 - 控制器模式是一个良好的开端。

模型视图控制器图

It can't be done perfectly in VBA due to the fact that we get either events or interfaces, never both, but you can get pretty close. 它不能在VBA中完美地完成,因为我们得到了事件或接口,而不是两者,但你可以非常接近。 Consider this simple form that has a text box and a button. 考虑这个带有文本框和按钮的简单表单。

简单的形式与文本框和按钮

In the form's code behind, we'll wrap the TextBox's value in a public property and re-raise any events we're interested in. 在后面的表单代码中,我们将TextBox的值包装在公共属性中,并重新引发我们感兴趣的任何事件。

Public Event OnSayHello()
Public Event AfterTextUpdate()

Public Property Let Text(value As String)
    Me.TextBox1.value = value
End Property

Public Property Get Text() As String
    Text = Me.TextBox1.value
End Property

Private Sub SayHello_Click()
    RaiseEvent OnSayHello
End Sub

Private Sub TextBox1_AfterUpdate()
    RaiseEvent AfterTextUpdate
End Sub

Now we need a model to work with. 现在我们需要一个模型来使用。 Here I've created a new class module named MyModel . 在这里,我创建了一个名为MyModel的新类模块。 Here lies the code we'll put under test. 这就是我们将要测试的代码。 Note that it naturally shares a similar structure as our view. 请注意,它自然地与我们的视图共享类似的结构。

Private mText As String
Public Property Let Text(value As String)
    mText = value
End Property

Public Property Get Text() As String
    Text = mText
End Property

Public Function Reversed() As String
    Dim result As String
    Dim length As Long

    length = Len(mText)

    Dim i As Long
    For i = 0 To length - 1
        result = result + Mid(mText, (length - i), 1)
    Next i

    Reversed = result
End Function

Public Sub SayHello()
    MsgBox Reversed()
End Sub

Finally, our controller wires it all together. 最后,我们的控制器将它们连接在一起。 The controller listens for form events and communicates changes to the model and triggers the model's routines. 控制器侦听表单事件并将更改传递给模型并触发模型的例程。

Private WithEvents view As Form_Form1
Private model As MyModel

Public Sub Run()
    Set model = New MyModel
    Set view = New Form_Form1
    view.Visible = True
End Sub

Private Sub view_AfterTextUpdate()
    model.Text = view.Text
End Sub

Private Sub view_OnSayHello()
    model.SayHello
    view.Text = model.Reversed()
End Sub

Now this code can be run from any other module. 现在,此代码可以从任何其他模块运行。 For the purposes of this example, I've used a standard module. 出于本示例的目的,我使用了标准模块。 I highly encourage you to build this yourself using the code I've provided and see it function. 我强烈建议您使用我提供的代码自己构建它并查看它的功能。

Private controller As FormController

Public Sub Run()
    Set controller = New FormController
    controller.Run
End Sub

So, that's great and all but what does it have to do with testing?! 那么,那很好, 除了与测试有什么关系之外呢?! Friend, it has everything to do with testing. 朋友,它具有一切与测试。 What we've done is make our code testable . 我们所做的就是让我们的代码可以测试 In the example I've provided, there is no reason what-so-ever to even try to test the GUI. 在我提供的示例中,甚至没有理由尝试测试GUI。 The only thing we really need to test is the model . 我们唯一需要测试的是model That's where all of the real logic is. 这就是所有真实逻辑的所在。

So, on to step two. 所以,继续第二步。

2. Choose a Unit Testing Framework 2.选择单元测试框架

There aren't a lot of options here. 这里没有很多选择。 Most frameworks require installing COM Add-ins, lots of boiler plate, weird syntax, writing tests as comments, etc. That's why I got involved in building one myself , so this part of my answer isn't impartial, but I'll try to give a fair summary of what's available. 大多数框架需要安装COM加载项,大量的样板,奇怪的语法,写测试作为评论等等。这就是为什么我自己参与构建一个 ,所以这部分答案不公正,但我会尝试对可用的内容进行公平的总结。

  1. AccUnit AccUnit

    • Works only in Access. 仅适用于Access。
    • Requires you to write tests as a strange hybrid of comments and code. 要求您将测试编写为注释和代码的奇怪组合。 (no intellisense for the comment part. (评论部分没有智能感知。
    • There is a graphical interface to help you write those strange looking tests though. 一个图形界面来帮你写那些奇怪的看着测试虽然。
    • The project has not seen any updates since 2013. 该项目自2013年以来未见任何更新。
  2. VB Lite Unit I can't say I've personally used it. VB Lite Unit我不能说我亲自使用它。 It's out there, but hasn't seen an update since 2005. 它在那里,但自2005年以来没有看到更新。

  3. xlUnit xlUnit isn't awful, but it's not good either. xlUnit xlUnit并不糟糕,但它也不好。 It's clunky and there's lots of boiler plate code. 它很笨重,并且有很多锅炉板代码。 It's the best of the worst, but it doesn't work in Access. 这是最糟糕的,但它在Access中不起作用。 So, that's out. 所以,那就是了。

  4. Build your own framework 建立自己的框架

    I've been there and done that . 去过那里并做到了 It's probably more than most people want to get into, but it is completely possible to build a Unit Testing framework in Native VBA code. 它可能比大多数人想要进入的更多,但完全有可能在Native VBA代码中构建单元测试框架。

  5. Rubberduck VBE Add-In's Unit Testing Framework Rubberduck VBE加载项的单元测试框架
    Disclaimer: I'm one of the co-devs . 免责声明:我是其中一个共同开发者

    I'm biased, but this is by far my favorite of the bunch. 我有偏见,但这是迄今为止我最喜欢的一群。

    • Little to no boiler plate code. 很少甚至没有锅炉板代码。
    • Intellisense is available. Intellisense可用。
    • The project is active. 该项目很活跃。
    • More documentation than most of these projects. 比大多数这些项目更多的文档。
    • It works in most of the major office applications, not just Access. 它适用于大多数主要的办公应用程序,而不仅仅是Access。
    • It is, unfortunately, a COM Add-In, so it has to be installed onto your machine. 遗憾的是,它是一个COM加载项,因此必须安装到您的计算机上。

3. Start writing tests 3.开始编写测试

So, back to our code from section 1. The only code that we really needed to test was the MyModel.Reversed() function. 所以,回到第1节中的代码。我们真正需要测试的唯一代码是MyModel.Reversed()函数。 So, let's take a look at what that test could look like. 那么,让我们来看看测试的样子。 (Example given uses Rubberduck, but it's a simple test and could translate into the framework of your choice.) (给出的示例使用Rubberduck,但这是一个简单的测试,可以转换为您选择的框架。)

'@TestModule
Private Assert As New Rubberduck.AssertClass

'@TestMethod
Public Sub ReversedReversesCorrectly()

Arrange:
    Dim model As New MyModel
    Const original As String = "Hello"
    Const expected As String = "olleH"
    Dim actual As String

    model.Text = original

Act:
    actual = model.Reversed

Assert:
    Assert.AreEqual expected, actual

End Sub

Guidelines for Writing Good Tests 编写良好测试指南

  1. Only test one thing at a time. 一次只测试一件事。
  2. Good tests only fail when there is a bug introduced into the system or the requirements have changed. 只有在系统中引入了错误或需求发生变化时,良好的测试才会失败。
  3. Don't include external dependencies such as databases and file systems. 不要包含外部依赖项,例如数据库和文件系统。 These external dependencies can make tests fail for reasons outside of your control. 这些外部依赖项可能会因为您无法控制的原因而导致测试失败。 Secondly, they slow your tests down. 其次,它们会减慢您的测试速度。 If your tests are slow, you won't run them. 如果你的测试很慢,你就不会运行它们。
  4. Use test names that describe what the test is testing. 使用描述测试测试内容的测试名称。 Don't worry if it gets long. 如果它变长,不要担心。 It's most important that it is descriptive. 最重要的是它是描述性的。

I know that answer was a little long, and late, but hopefully it helps some people get started in writing unit tests for their VBA code. 我知道答案有点长,而且很晚,但希望它可以帮助一些人开始为他们的VBA代码编写单元测试。

I appreciated knox's and david's answers. 我很欣赏诺克斯和大卫的答案。 My answer will be somewhere between theirs: just make forms that do not need to be debugged ! 我的答案将介于他们之间:只需制作不需要调试的表单

I think that forms should be exclusively used as what they are basically, meaning graphic interface only , meaning here that they do not have to be debugged! 我认为表单应该专门用作它们的基本内容, 仅指图形界面,这意味着它们不必调试! The debugging job is then limited to your VBA modules and objects, which is a lot easier to handle. 然后,调试作业仅限于您的VBA模块和对象,这样更容易处理。

There is of course a natural tendency to add VBA code to forms and/or controls, specially when Access offers you these great "after Update" and "on change" events, but I definitely advise you not to put any form or control specific code in the form's module. 当然有一种将VBA代码添加到表单和/或控件的自然趋势,特别是当Access为您提供这些伟大的“更新后”和“更改后”事件时,但我绝对建议您不要放置任何表单或控制特定代码在表单的模块中。 This makes further maintenance and upgrade very costy, where your code is split between VBA modules and forms/controls modules. 这使得进一步维护和升级变得非常昂贵,其中您的代码在VBA模块和表单/控件模块之间分配。

This does not mean you cannot use anymore this AfterUpdate event! 这并不意味着你不能再使用这个AfterUpdate事件了! Just put standard code in the event, like this: 只需将标准代码放入事件中,如下所示:

Private Sub myControl_AfterUpdate()  
    CTLAfterUpdate myControl
    On Error Resume Next
    Eval ("CTLAfterUpdate_MyForm()")
    On Error GoTo 0  
End sub

Where: 哪里:

  • CTLAfterUpdate is a standard procedure run each time a control is updated in a form CTLAfterUpdate是每次在表单中更新控件时运行的标准过程

  • CTLAfterUpdateMyForm is a specific procedure run each time a control is updated on MyForm CTLAfterUpdateMyForm是每次在MyForm上更新控件时运行的特定过程

I have then 2 modules. 我有2个模块。 The first one is 第一个是

  • utilityFormEvents
    where I will have my CTLAfterUpdate generic event 我将在哪里获得CTLAfterUpdate通用事件

The second one is 第二个是

  • MyAppFormEvents
    containing the specific code of all specific forms of the MyApp application and including the CTLAfterUpdateMyForm procedure. 包含MyApp应用程序的所有特定形式的特定代码,包括CTLAfterUpdateMyForm过程。 Of course, CTLAfterUpdateMyForm might not exist if there are no specific code to run. 当然,如果没有特定的代码可以运行,CTLAfterUpdateMyForm可能不存在。 This is why we turn the "On error" to "resume next" ... 这就是为什么我们将“On error”变为“resume next”......

Choosing such a generic solution means a lot. 选择这样的通用解决方案意味着很多。 It means you are reaching a high level of code normalization (meaning painless maintenance of code). 这意味着您正在达到高级别的代码规范化(意味着无痛的代码维护)。 And when you say that you do not have any form-specific code, it also means that form modules are fully standardized, and their production can be automated : just say which events you want to manage at the form/control level, and define your generic/specific procedures terminology. 当你说你没有任何特定于表单的代码时,它也意味着表单模块是完全标准化的,并且它们的生产可以自动化 :只需说明你想在表单/控件级别管理哪些事件,并定义你的通用/特定程序术语。
Write your automation code, once for all. 一劳永逸地编写自动化代码。
It takes a few days of work but it give exciting results. 这需要几天的工作,但它会带来令人兴奋的结果。 I have been using this solution for the last 2 years and it is clearly the right one: my forms are fully and automatically created from scratch with a "Forms Table", linked to a "Controls Table". 在过去的两年里,我一直在使用这个解决方案,它显然是正确的:我的表单是从头开始完全自动创建的“表格表”,链接到“控制表”。
I can then spend my time working on the specific procedures of the form, if any. 然后,我可以花时间研究表单的特定过程(如果有的话)。

Code normalization, even with MS Access, is a long process. 即使使用MS Access,代码规范化也是一个漫长的过程。 But it is really worth the pain! 但这真的值得痛苦!

Another advantage of Access being a COM application is that you can create an .NET application to run and test an Access application via Automation . Access作为COM应用程序的另一个优点是,您可以创建.NET应用程序以通过Automation运行和测试Access应用程序 The advantage of this is that then you can use a more powerful testing framework such as NUnit to write automated assert tests against an Access app. 这样做的好处是,您可以使用更强大的测试框架(如NUnit)来编写针对Access应用程序的自动断言测试。

Therefore, if you are proficient in either C# or VB.NET combined with something like NUnit then you can more easily create greater test coverage for your Access app. 因此,如果您熟练使用C#或VB.NET以及NUnit之类的东西,那么您可以更轻松地为Access应用程序创建更大的测试覆盖率。

Although that being a very old answer: 虽然这是一个非常古老的答案:

There is AccUnit , a specialized Unit-Test framework for Microsoft Access. AccUnit ,一个专门用于Microsoft Access的单元测试框架。

I've taken a page out of Python's doctest concept and implemented a DocTests procedure in Access VBA. 我从Python的doctest概念中获取了一个页面,并在Access VBA中实现了DocTests过程。 This is obviously not a full-blown unit-testing solution. 这显然不是一个完整的单元测试解决方案。 It's still relatively young, so I doubt I've worked out all the bugs, but I think it's mature enough to release into the wild. 它仍然相对年轻,所以我怀疑我已经解决了所有的错误,但我认为它足够成熟,可以释放到野外。

Just copy the following code into a standard code module and press F5 inside the Sub to see it in action: 只需将以下代码复制到标准代码模块中,然后在Sub中按F5即可查看其中的操作:

'>>> 1 + 1
'2
'>>> 3 - 1
'0
Sub DocTests()
Dim Comp As Object, i As Long, CM As Object
Dim Expr As String, ExpectedResult As Variant, TestsPassed As Long, TestsFailed As Long
Dim Evaluation As Variant
    For Each Comp In Application.VBE.ActiveVBProject.VBComponents
        Set CM = Comp.CodeModule
        For i = 1 To CM.CountOfLines
            If Left(Trim(CM.Lines(i, 1)), 4) = "'>>>" Then
                Expr = Trim(Mid(CM.Lines(i, 1), 5))
                On Error Resume Next
                Evaluation = Eval(Expr)
                If Err.Number = 2425 And Comp.Type <> 1 Then
                    'The expression you entered has a function name that ''  can't find.
                    'This is not surprising because we are not in a standard code module (Comp.Type <> 1).
                    'So we will just ignore it.
                    GoTo NextLine
                ElseIf Err.Number <> 0 Then
                    Debug.Print Err.Number, Err.Description, Expr
                    GoTo NextLine
                End If
                On Error GoTo 0
                ExpectedResult = Trim(Mid(CM.Lines(i + 1, 1), InStr(CM.Lines(i + 1, 1), "'") + 1))
                Select Case ExpectedResult
                Case "True": ExpectedResult = True
                Case "False": ExpectedResult = False
                Case "Null": ExpectedResult = Null
                End Select
                Select Case TypeName(Evaluation)
                Case "Long", "Integer", "Short", "Byte", "Single", "Double", "Decimal", "Currency"
                    ExpectedResult = Eval(ExpectedResult)
                Case "Date"
                    If IsDate(ExpectedResult) Then ExpectedResult = CDate(ExpectedResult)
                End Select
                If (Evaluation = ExpectedResult) Then
                    TestsPassed = TestsPassed + 1
                ElseIf (IsNull(Evaluation) And IsNull(ExpectedResult)) Then
                    TestsPassed = TestsPassed + 1
                Else
                    Debug.Print Comp.Name; ": "; Expr; " evaluates to: "; Evaluation; " Expected: "; ExpectedResult
                    TestsFailed = TestsFailed + 1
                End If
            End If
NextLine:
        Next i
    Next Comp
    Debug.Print "Tests passed: "; TestsPassed; " of "; TestsPassed + TestsFailed
End Sub

Copying, pasting, and running the above code from a module named Module1 yields: 从名为Module1的模块复制,粘贴和运行上述代码会产生:

Module: 3 - 1 evaluates to:  2  Expected:  0 
Tests passed:  1  of  2

A few quick notes: 一些快速说明:

  • It has no dependencies (when used from within Access) 它没有依赖关系(从Access中使用时)
  • It makes use of Eval which is a function in the Access.Application object model; 它使用了Eval ,这是Access.Application对象模型中的一个函数; this means you could use it outside of Access but it would require creating an Access.Application object and fully qualifying the Eval calls 这意味着您可以在Access之外使用它,但它需要创建一个Access.Application对象并完全限定Eval调用
  • There are some idiosyncrasies associated with Eval to be aware of 有一些Eval相关的特性需要注意
  • It can only be used on functions that return a result that fits on a single line 它只能用于返回适合单行的结果的函数

Despite its limitations, I still think it provides quite a bit of bang for your buck. 尽管有其局限性,我仍然认为它为你的降压提供了相当多的帮助。

Edit : Here is a simple function with "doctest rules" the function must satisfy. 编辑 :这是一个简单的功能与“doctest规则”功能必须满足。

Public Function AddTwoValues(ByVal p1 As Variant, _
        ByVal p2 As Variant) As Variant
'>>> AddTwoValues(1,1)
'2
'>>> AddTwoValues(1,1) = 1
'False
'>>> AddTwoValues(1,Null)
'Null
'>>> IsError(AddTwoValues(1,"foo"))
'True

On Error GoTo ErrorHandler

    AddTwoValues = p1 + p2

ExitHere:
    On Error GoTo 0
    Exit Function

ErrorHandler:
    AddTwoValues = CVErr(Err.Number)
    GoTo ExitHere
End Function

I would design the application to have as much work as possible done in queries and in vba subroutines so that your testing could be made up of populating test databases, running sets of the production queries and vba against those databases and then looking at the output and comparing to make sure the output is good. 我将设计应用程序以在查询和vba子例程中完成尽可能多的工作,以便您的测试可以由填充测试数据库,运行生产查询集和对这些数据库的vba组成,然后查看输出和比较以确保输出良好。 This approach doesn't test the GUI obviously, so you could augment the testing with a series of test scripts (here I mean like a word document that says open form 1, and click control 1) that are manually executed. 这种方法显然不会对GUI进行测试,因此您可以使用一系列测试脚本来扩充测试(这里我的意思是像开放式1的单词文档,然后单击控件1),这些脚本是手动执行的。

It depends on the scope of the project as the level of automation necessary for the testing aspect. 它取决于项目的范围,作为测试方面所需的自动化水平。

I find that there are relatively few opportunities for unit testing in my applications. 我发现在我的应用程序中进行单元测试的机会相对较少。 Most of the code that I write interacts with table data or the filing system so is fundamentally hard to unit test. 我编写的大多数代码都与表数据或文件系统交互,因此从根本上难以进行单元测试。 Early on, I tried an approach that may be similar to mocking (spoofing) where I created code that had an optional parameter. 在早期,我尝试了一种类似于模拟(欺骗)的方法,其中我创建了具有可选参数的代码。 If the parameter was used, then the procedure would use the parameter instead of fetching data from the database. 如果使用了该参数,则该过程将使用该参数而不是从数据库中获取数据。 It is quite easy to set up a user defined type that has the same field types as a row of data and to pass that to a function. 设置用户定义的类型非常容易,该类型具有与一行数据相同的字段类型并将其传递给函数。 I now have a way of getting test data into the procedure that I want to test. 我现在有办法将测试数据输入到我想要测试的过程中。 Inside each procedure there was some code that swapped out the real data source for the test data source. 在每个过程中都有一些代码可以替换测试数据源的真实数据源。 This allowed me to use unit testing on a wider variety of function, using my own unit testing functions. 这使我能够使用我自己的单元测试功能对更广泛的功能进行单元测试。 Writing unit test is easy, it is just repetitive and boring. 写单元测试很简单,只是重复和无聊。 In the end, I gave up with unit tests and started using a different approach. 最后,我放弃了单元测试并开始使用不同的方法。

I write in-house applications for myself mainly so I can afford wait till issues find me rather than having to have perfect code. 我主要为自己编写内部应用程序,因此我可以等到问题找到我而不是必须拥有完美的代码。 If I do write applications for customers, generally the customer is not fully aware of how much software development costs so I need a low cost way of getting results. 如果我为客户编写应用程序,通常客户并不完全了解软件开发成本,因此我需要一种低成本的方法来获得结果。 Writing unit tests is all about writing a test that pushes bad data at a procedure to see if the procedure can handle it appropriately. 编写单元测试就是编写一个测试,在一个过程中推送坏数据,以查看过程是否可以正确处理它。 Unit tests also confirm that good data is handled appropriately. 单元测试还确认正确处理好的数据。 My current approach is based on writing input validation into every procedure within an application and raising a success flag when the code has completed successfully. 我当前的方法是基于将输入验证写入应用程序中的每个过程,并在代码成功完成时引发成功标志。 Each calling procedure checks for the success flag before using the result. 在使用结果之前,每个调用过程都会检查成功标志。 If an issue occurs, it is reported by way of an error message. 如果出现问题,则通过错误消息报告。 Each function has a success flag, a return value, an error message, a comment and an origin. 每个函数都有一个成功标志,一个返回值,一个错误消息,一个注释和一个原点。 A user defined type (fr for function return) contains the data members. 用户定义的类型(函数返回的fr)包含数据成员。 Any given function many populate only some of the data members in the user defined type. 任何给定的函数都只填充用户定义类型中的某些数据成员。 When a function is run, it usually returns success = true and a return value and sometimes a comment. 运行函数时,它通常会返回success = true和返回值,有时还会返回注释。 If a function fails, it returns success = false and an error message. 如果函数失败,则返回success = false和错误消息。 If a chain of functions fails, the error messages are daisy changed but the result is actually a lot more readable that a normal stack trace. 如果一系列函数失败,则会更改错误消息,但结果实际上比正常的堆栈跟踪更具可读性。 The origins are also chained so I know where the issue occurred. 起源也是链接的,所以我知道问题出在哪里。 The application rarely crashes and accurately reports any issues. 该应用程序很少崩溃并准确报告任何问题。 The result is a hell of a lot better than standard error handling. 结果是比标准错误处理好得多。

Public Function GetOutputFolder(OutputFolder As eOutputFolder) As  FunctRet

        '///Returns a full path when provided with a target folder alias. e.g. 'temp' folder

            Dim fr As FunctRet

            Select Case OutputFolder
            Case 1
                fr.Rtn = "C:\Temp\"
                fr.Success = True
            Case 2
                fr.Rtn = TrailingSlash(Application.CurrentProject.path)
                fr.Success = True
            Case 3
                fr.EM = "Can't set custom paths – not yet implemented"
            Case Else
                fr.EM = "Unrecognised output destination requested"
            End Select

    exitproc:
        GetOutputFolder = fr

    End Function

Code explained. 代码解释。 eOutputFolder is a user defined Enum as below eOutputFolder是用户定义的枚举,如下所示

Public Enum eOutputFolder
    eDefaultDirectory = 1
    eAppPath = 2
    eCustomPath = 3
End Enum

I am using Enum for passing parameters to functions as this creates a limited set of known choices that a function can accept. 我正在使用Enum将参数传递给函数,因为这会创建函数可以接受的一组有限的已知选项。 Enums also provide intellisense when entering parameters into functions. 枚举在向函数输入参数时也提供智能感知。 I suppose they provide a rudimentary interface for a function. 我想它们为函数提供了一个基本的接口。

'Type FunctRet is used as a generic means of reporting function returns
Public Type  FunctRet
    Success As Long     'Boolean flag for success, boolean not used to avoid nulls
    Rtn As Variant      'Return Value
    EM As String        'Error message
    Cmt As String       'Comments
    Origin As String    'Originating procedure/function
End Type

A user defined type such as a FunctRet also provides code completion which helps. 用户定义的类型(如FunctRet)也提供了有用的代码完成功能。 Within the procedure, I usually store internal results to an anonymous internal variable (fr) before assigning the results to the return variable (GetOutputFolder). 在该过程中,我通常将内部结果存储到匿名内部变量(fr),然后将结果分配给返回变量(GetOutputFolder)。 This makes renaming procedures very easy as only the top and bottom have be changed. 这使得重命名过程非常简单,因为只需更改顶部和底部。

So in summary, I have developed a framework with ms-access that covers all operations that involve VBA. 总而言之,我开发了一个带有ms-access的框架,涵盖了涉及VBA的所有操作。 The testing is permanently written into the procedures, rather than a development time unit test. 测试永久写入程序,而不是开发时间单元测试。 In practice, the code still runs very fast. 在实践中,代码仍然运行得非常快。 I am very careful to optimise lower level functions that can be called ten thousand times a minute. 我非常小心地优化可以每分钟调用一万次的低级功能。 Furthermore, I can use the code in production as it is being developed. 此外,我可以在开发过程中使用生产中的代码。 If an error occurs, it is user friendly and the source and reason for the error are usually obvious. 如果发生错误,则用户友好,错误的来源和原因通常很明显。 Errors are reported from the calling form, not from some module in the business layer, which is an important principal of application design. 从调用表单报告错误,而不是从业务层中的某个模块报告,这是应用程序设计的重要原则。 Furthermore, I don't have the burden of maintaining unit testing code, which is really important when I am evolving a design rather than coding a clearly conceptualised design. 此外,我没有维护单元测试代码的负担,这在我开发设计而不是编写清晰概念化设计时非常重要。

There are some potential issues. 有一些潜在的问题。 The testing is not automated and new bad code is only detected when the application is run. 测试不是自动化的,只有在运行应用程序时才会检测到新的错误代码。 The code does not look like standard VBA code (it is usually shorter). 代码看起来不像标准VBA代码(通常更短)。 Still, the approach has some advantages. 不过,这种方法有一些优点。 It is far better that using an error handler just to log an error as the users will usually contact me and give me a meaningful error message. 使用错误处理程序来记录错误要好得多,因为用户通常会联系我并给我一个有意义的错误消息。 It can also handle procedures that work with external data. 它还可以处理与外部数据一起使用的过程。 JavaScript reminds me of VBA, I wonder why JavaScript is the land of frameworks and VBA in ms-access is not. JavaScript让我想起了VBA,我想知道为什么JavaScript是框架的土地,而ms-access中的VBA则不然。

A few days after writing this post, I found an article on The CodeProject that comes close to what I have written above. 写完这篇文章几天后,我发现了一篇关于The CodeProject文章 ,它与我上面写的内容很接近。 The article compares and contrasts exception handling and error handling. 本文比较和对比了异常处理和错误处理。 What I have suggested above is akin to exception handling. 我上面提到的内容类似于异常处理。

如果您有兴趣在更细粒度级别测试Access应用程序,特别是VBA代码本身,那么VB Lite Unit就是一个很好的单元测试框架。

There are good suggestions here, but I'm surprised no one mentioned centralized error processing. 这里有很好的建议,但我很惊讶没有人提到集中式错误处理。 You can get addins that allow for quick function/sub templating and for adding line numbers (I use MZ-tools). 您可以获得允许快速功能/子模板和添加行号的插件(我使用MZ工具)。 Then send all errors to a single function where you can log them. 然后将所有错误发送到您可以记录它们的单个函数。 You can also then break on all errors by setting a single break point. 然后,您还可以通过设置单个断点来中断所有错误。

I have not tried this, but you could attempt to publish your access forms as data access web pages to something like sharepoint or just as web pages and then use an tool such as selenium to drive the browser with a suite of tests. 我没有尝试过,但您可以尝试将访问表单作为数据访问网页发布到sharepointweb页面 ,然后使用selenium等工具通过一系列测试来驱动浏览器。

Obviously this is not as ideal as driving the code directly through unit tests, but it may get you part of the way. 显然,这不像通过单元测试直接驱动代码那样理想,但它可能会让你成为一部分。 good luck 祝好运

Access is a COM application. Access是一个COM应用程序。 Use COM, not Windows API. 使用COM,而不是Windows API。 to test things in Access. 在Access中测试东西。

The best Test environment for an Access Application is Access. Access应用程序的最佳测试环境是Access。 All of your Forms/Reports/Tables/Code/Queries are available, there is a scripting language similar to MS Test (Ok, you probably don't remember MS Test), there is database environment for holding your test scripts and test results, and the skills you build here are transferable to your application. 您可以使用所有表单/报表/表格/代码/查询,有类似于MS Test的脚本语言(好的,您可能不记得MS Test),有用于保存测试脚本和测试结果的数据库环境,您在此处构建的技能可以转移到您的应用程序中。

Data Access Pages have been deprecated by MS for quite some time, and never really worked in the first place (they were dependent on the Office Widgets being installed, and worked only in IE, and only badly then). 数据访问页面已被MS弃用了相当长的一段时间,并且从未真正起作用(它们依赖于正在安装的Office Widgets,并且仅在IE中工作,并且只是非常糟糕)。

It is true that Access controls that can get focus only have a window handle when they have the focus (and those that can't get focus, such as labels, never have a window handle at all). 确实,可以获得焦点的Access控件在有焦点时只有一个窗口句柄(那些无法获得焦点的控件,如标签,根本就没有窗口句柄)。 This makes Access singularly inappropriate to window handle-driven testing regimes. 这使得Access非常不适合窗口句柄驱动的测试机制。

Indeed, I question why you want to do this kind of testing in Access. 实际上,我怀疑你为什么要在Access中进行这种测试。 It sounds to me like your basic Extreme Programming dogma, and not all of the principles and practices of XP can be adapted to work with Access applications -- square peg, round hole. 这听起来像你的基本极限编程教条,并不是XP的所有原理和实践都可以适用于Access应用程序 - 方形钉,圆孔。

So, step back and ask yourself what you're trying to accomplish and consider that you may need to utilize completely different methods than those that are based on the approaches that just can't work in Access. 所以,退后一步,问问自己你想要完成什么,并认为你可能需要使用完全不同的方法,而不是基于在Access中无法使用的方法。

Or whether that kind of automated testing is valid at all or even useful with an Access application. 或者这种自动化测试是否完全有效,甚至对Access应用程序是否有用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM