views:

1431

answers:

8

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).

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.

Any experience to share?

+3  A: 

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. 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.

It depends on the scope of the project as the level of automation necessary for the testing aspect.

Knox
A: 

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.

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

Xian
A: 

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).

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). This makes Access singularly inappropriate to window handle-driven testing regimes.

Indeed, I question why you want to do this kind of testing in 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.

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.

Or whether that kind of automated testing is valid at all or even useful with an Access application.

--
David W. Fenton
http://dfenton.com/DFA/

David-W-Fenton
Well, if, like me, you use Access as a framework to build complex applications then at some point you do need testing.It's not just a 'Xtreme programming' thing, it's not a fad, it's a necessity: I'm changing code in one place and I need to know if I've broken something else.
Renaud Bompuis
I didn't downplay the value of testing. I only criticized the application of *automated* testing to Access applications.
David-W-Fenton
+1  A: 

Access is a COM application. Use COM, not Windows API. to test things in Access.

The best Test environment for an Access Application is 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.

+5  A: 

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.

There is of course a natural tendancy 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. This makes further maintenance and upgrade very costy, where your code is split between VBA modules and forms/controls modules.

This does not mean you cannot use anymore this AfterUpdate event! 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

  • CTLAfterUpdateMyForm is a specific procedure run each time a control is updated on MyForm

I have then 2 modules. The first one is

  • utilityFormEvents
    where I will have my CTLAfterUpdate generic event

The second one is

  • MyAppFormEvents
    containing the specific code of all specific forms of the MyApp application and incuding the CTLAfterUpdateMyForm procedure. Of course, CTLAfterUpdateMyForm might not exist if there are no specific code to run. This is why we turn the "On error" to "resume next" ...

Choosing such a generic solution means a lot. It means you are reaching a high level of code normalisation (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 normalisation, even with MS Access, is a long process. But it is really worth the pain!

Philippe Grondier
This sounds interesting, why don't you publish some example somewhere?
GUI Junkie
@GUI Junkie, I'll keep you informed.
Philippe Grondier
Why not just put the afterupdate property to =myModule.AfterUpdate(me.controlname)? That way you get to write a nice generic function that gets passed a reference to the specific control without any eval magic. Or am I missing something?
Jauco
This is a way to avoid my "eval magic".But it'll force you to manage cases where there's a specific "afterUpdate" proc either by updating the control's property, or by adding a CASE line at the proc level. The "Eval" solution avoids this extra overhead. You just create the function when you need it.
Philippe Grondier
I would like to see a schema of the Forms and Controls tables you've mentioned. I don't quite understand how they'd work.
Knobloch
I think this will be my first publication on my expected blog ... I will announce it at http://stackoverflow.com/questions/286514
Philippe Grondier
+3  A: 

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. 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.

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.

Ray Vega
I have actually done this. I would strongly recommend this way as you then have all the strengths of .net to test your Access/VBA app.
KevinDeus
+2  A: 

If your interested in testing your Access application at a more granular level specifically the VBA code itself then VB Lite Unit is a great unit testing framework for that purpose.

Ray Vega
A: 

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). 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.

Steve