[ 1 ] [ 2 ] [ 3 ] [ 4 ] [ 5 ] [ 6 ] [ 7 ] [ 8 ]

Runner plus…: A hybrid approach

Just as a combination of testing techniques will net you a higher bug count, I feel a combination of automated testing techniques can help you cover all the bases.

I've written a VFP autorunning object I call " Runner" , which you can add to VFP applications or the VFP environment quite easily. Runner has both autotesting and self-running demo applications. It is a " smart" timer object, which works by scanning through a DBF of script actions that you prepare. For each type of action (and Runner's scripting action types encompass most of what you can do in VFP), Runner disables itself until the action completes and then re-enables to execute its next script action.

Allowable Runner script actions run the gamut from mouse-handling through execution of entire programs you indicate. Runner understands the grouping of actions into something I call " phases" , and you can designate phases, or single action records, to be repeated as required. Special provisions are made for logging, both of errors encountered or of messages you designate as the script progresses. I'd also suggest you think about running some of Steven Black's state-snapshot-dumping methods via a Runner action at various points in your script.

In view of Runner's timer implementation, the most problematic actions are actions that return to Runner before the action completes. These actions include both native PLAY MACRO statements and actions that run an external Macro or scripting facility.

Briefly, you need to provide Runner with some help to wait until extensive macros complete, whether they're external or internal. I've got a few suggestions for you that will help. (You can actually see one of the suggestions under construction in the figure showing MacroScheduler above. See RUNNER.TXT in the \RUNNER folder for more information.) Although these suggestions work, it's also best to set your Runner Interval high enough to allow for these actions to " proceed naturally" if possible. You can even temporarily adjust Runner's Interval just before and after any macro-running script action.

I've made provisions to integrate Macro Scheduler into the Runner set of allowable action types. I think that this hybrid approach allows me to handle OLE objects, mouse menu objects, and other " tricky" actions with the greatest flexibility, while still allowing me VFP's full range of abilities in other respects. I would like to integrate other external tools into Runner scenarios as well.


NB: If you know you have access the Windows Scripting Host object on your test machines, you can avoid the problems with RUN and wait state handling. This is something I haven't had time to integrate with Runner yet, because I'd still need to ensure that this object was available and revert to RUN if it were not. In your environment, however, and your subclass of Runner, you can use the object's Run method without worrying about this.

There is very little else you have to know, to use Runner. I've prepared a demo application (in the \DEMORUN folder) with a demo Runner script table, so you can see how Runner works. Since the addition of a Runner object is really the only change I've made to a generated VFP 6 Framework app for this demo, I haven't bothered giving you the source. You'll only find the APP, its data, the Runner script table, and a macro file the Runner script loads.

There is no necessary connection between Runner and the framework used to create this demo application. Still, the small additions to the app_application object in this project will give you some extra help on integration of a Runner object into any type of application. Here are all the changes I made to the generated app_applicatio


NB: Since this demo project is a generated application, using the VFP 6 framework and application wizard, its objects refer to many other VFP 6 classes and FFC components. Since the demo app is a VFP 6-framework application, you can't run the demo in VFP 5. However, I think Runner and its FRONTRUN.SCX delivery device, described later in this section, will work fine in VFP 5, if you need it there.

n object in the demo framework project:

  1. I added a Runner member to the app_application container.
  2. I edited the Runner member's Error method, to give the application's general error handling precedence if an error occurred within Runner code itself (this is not a requirement, just a personal preference):
LPARAMETERS nerror,cmethod,nline
IF nError # 1540
   * I know there is going to be an error of
   * this type on the way out, and in
   * this context the error is meaningless
  1. I made sure the Runner object would know when an error occurred within the application I was testing, by adjusting the app_application's Error method. This is something you should always find a way to do, if possible, because you may need to skip over some actions after the current action if an error occurs. Because of Runner's Phase concept, which groups actions into separate tasks, you can still go on with the rest of the script after the current Phase:
LPARAMETERS nerror,cmethod,nline
THIS.cusRunner.lError = .T.
  1. I gave the app three new custom properties: cRunnerDBF, iAutoRunInterval and lInAutoRunMode. I added the name of my script table as the value of my cRunnerDBF property, and set an appropriate default interval as the value of the iAutoRunInterval property.
  2. I created an access method for lInAutoRunMode, as follows:
RETURN THIS.iAutoRunInterval > 0  ;
       AND VARTYPE(THIS.cRunnerDBF) = " C"  AND  ;
      (FILE(THIS.cRunnerDBF) OR ;
       FILE(THIS.cRunnerDBF+" .DBF" ))
  1. Now I adjust app_application startup procedures to handle an " auto run" mode, by putting this code in the BeforeReadEvents() method. If the application should " autorun" , the values of the cRunnerDBF and iAutoRunInterval properties of the application object are applied to its Runner member' properties, and the Run() begins:
    LOCAL llReturn
    llReturn = DODEFAULT()
    IF llReturn AND THIS.lInAutoRunMode
       THIS.cusError.lServer = .T.
       * the above is a convenient way
       * to make sure that the app
       * keeps running after logging app errors -- not strictly
       * necessary but it helps make sure that
       * Runner goes on to the next phase smoothly if
       * an error occurs in the app.
       * I've also augmented the Error
       * event of this app's Runner member to call
       * the app's error method, so that error logging occurs
       * even if a Runner method causes the error.
       * The lServer mode of the cusError object will
       * make sure we don't get " stuck"  in error dialogs if
       * this happens.
       WITH THIS.cusRunner
           * here you could make an evaluation --
           * is this an autotest (set the interval fairly
           * low) or is it a Demo run (set it higher,
           * so people can see what's going on)
           .cTable = THIS.cRunnerDBF
           .Interval = THIS.iAutoRunInterval
    RETURN THIS.IsErrorFree() AND llReturn
  1. Because it didn't make sense to me to show the Quick Start form at the beginning of the application if I was trying to create an " auto run" of the app, I added this code in the app_application's ShowStartupElements() method (this is completely optional and specific to the framework as well):
IF THIS.lInAutoRunMode
   THIS.lStartupForm = .F.
   * It just seems like a reasonable thing
   * to do!

As a general rule, integration of Runner with your application -- however your application is organized --consists of these factors, consistent with the changes I've just listed:

  1. proper recognition by the application of when it should, and should not, be in " auto mode"
  2. some adjustment of your application's error handling (if you have not otherwise provided for this) to run without user intervention when necessary. (The demonstration script table for the DEMO project includes a deliberate error, so you can see how Runner attempts to recover from any such event.)
  3. optional integration of Runner's Error event with your application's error handler, so that any intra-Runner error calls the same error logging system that normal application errors would trigger.
  4. optional recognition by any application elements that they are in a " special situation" when in " auto mode" , such as the adjustments to the lServer property, switching error handling to WAIT WINDOW NOWAITs from MESSAGEBOX()s, and the change to lStartupForm, that you see in the example code above.

In the DEMORUN example script table, an error is deliberately caused so that you can see how Runner exits an error-causing phase and attempts to continue on with the rest of its script. After the error, part of the script shows off some of the framework's capabilities, by bringing forward the error log viewer and dynamically changing its font using the options dialog.

Watch your VFP status bar at the end of the Demorun. If you have the status bar on, you should see Runner continuing to go through some additional records before it finally finishes. Afterwards you can examine its *.RUN log, to see what happened.

During the run, WAIT WINDOWs also show up to describe what's going on - these are INFO instructions in the demo script itself. Runner's INFO action type, which creates these WAIT WINDOWs, is very useful for both testing and demo purposes.

The hard part of using Runner is not its simple API. You'll find this documented in RUNNER.TXT. (Runner is very much a work-in-progress, and RUNNER.TXT is " it" for the moment, and it's not pretty. But it is thorough!)

The hard part of properly testing with Runner is the same as it is for any automated testing: you have to devise appropriate testing scenarios and then write the scripts to automate these procedures.

FRONTRUN.SCX (Front Runner) is a tool to help you write Runner scripts.

For this reason, I'm delivering a simple front end to Runner script writing, which I call Front Runner. If you DO FORM FRONTRUN (in the \RUNNER folder), you'll find that it examines and creates Runner scripts without much validation for your " script action" entries but with a table-driven approach to allowable action types, and information on each type available. In the figure above , you can see the DEMORUN\DEMORUN.DBF script table under edit.

You'll need more help…

Well, it's a start.

This article and the tools represented herein are only a beginning. COVERAGE.APP facilities in VFP 6 are a big help, but as you've already seen you'll need to interpret and use the results before your applications will benefit.

Luckily, as I hope you've also already seen, VFP is such a brilliant environment in which to write tools, and in which to analyze results, that you are a leg up on the competition before you begin!

It can truly be said that testing is not quality assurance. Use of what you learn from tests, deployment of testing information accurately and with constructive care, results in quality.

When used properly, testing becomes an important part of a full quality-assurance process. You'll need a practical plan to encourage testing, and testers, in your organization. I've enjoyed writing these notes to give you some ideas.

Be sure to drop by http://www.softspoken.co.nz/lisa_fox.htm, or write a message on the msnews.microsoft.com public fox groups, to share more ideas with me and with the Fox community.

[ 1 ] [ 2 ] [ 3 ] [ 4 ] [ 5 ] [ 6 ] [ 7 ] [ 8 ]