Re: how do you test GUI functionality?

From: John Seal (johnseal_at_indy.net)
Date: 11/05/03

  • Next message: Glenn Jackman: "Re: How to count the repeating elements in a set of data"
    Date: Wed, 05 Nov 2003 01:28:20 GMT
    
    

    In article <2VTpb.1572$z04.432@newssvr22.news.prodigy.com>,
     Bryan Oakley <bryan@bitmover.com> wrote:

    > For those of you who write applications with Tk, how do you
    > test the functionality of your applications?

    I wrote a small test harness called Classic Automated Regression Test
    (CART; Classic is the name of the application). It uses comm to work
    the app by remote control without permanently injecting any debug code.
    It is implemented as a package, and exports procedures for grabbing
    resources from a hardware pool, pushing buttons (any kind, even menu
    commands), entering values, providing simulated inputs, extracting state
    data, and testing results.

    Individual tests are Tcl scripts which primarily call our exported
    domain-specific cart:: procedures, but they can use the full power of
    Tcl. The test harness takes care of teeing all stdout into serialized
    log files, with timestamps on each line. Tests can be grouped together
    into larger test suites. The exported functions of cart:: do a fairly
    good job of insulating the test developer from needing intimate
    knowledge of the app's internal structure, but I'm afraid that testing
    certain values requires at least a knowledge of the variable names.

    This is for an interrupt-driven realtime control application with an
    *extremely* complex GUI, with a server that talks to the equipment via
    GPIB and dozens of clients distributed over many remote hosts. Total
    code on the order of 5000 SLOC.

    So far I've only used it "officially" for unit testing. Some people are
    impressed, but it's not clear if I'll be allowed to develop a full
    regression test suite, as I hope.

    I have one script which tests handing off control from one GUI to
    another; it's fascinating to watch two GUIs open on the screen,
    reposition themselves to be side-by-side, buttons magically pushing on
    one GUI to setup the scenario, then handing off to the other GUI. A log
    window keeps a running commentary on what's happening, and if the states
    of the GUIs match at the end, a PASS message is generated. Afterwards,
    the same info is there in the log file, with timestamps. Sweet!

    For an example, here is the source for the button-pushing procedure:

    # Returns a script to push a specified button in a GUI.

    # The buttonName is the text of the button as it appears in the GUI.
    # This procedure DOES NOT push the button; that doesn't happen until
    # you send the resulting script to the GUI for evaluation.

    proc cart::push {buttonName} {
        # This whole thing is the script, all we do is plug in buttonName.
        # Note the extensive use of [set] to avoid premature $ substitution.
        # The script walks the widget tree by keeping a list of unvisited
        # nodes. Each node is removed from the list as it is examined, and
    its
        # children are added. The first widget whose -text option matches the
        # specified buttonName is invoked, and its pathname is returned.
        subst -nocommands {
            set places {.}
            while {[llength [set places]]} {
                set place [lindex [set places] 0]
                set places [lrange [set places] 1 end]
                set places [concat [set places] [winfo children [set place]]]
                if {[catch {[set place] cget -text} text]} {continue}
                if {![string equal "$buttonName" [set text]]} {continue}
                [set place] invoke
                return [set place]
            }
        }
    }

    And here is a silly pseudocode example of how it might be used:

    # Test script for "Doctor" application.
    package require cart
    namespace import cart::*
    set n [need 1 patient]
    patient $n [push "Get Sick"]
    patient $n [enter temp 101]
    patient $n [push "Take Aspirin"]
    verify {[patient $n {set temp}] == 98.6} "Aspirin didn't reduce fever!"
    pass "Temperature is normal"

    Note the use of braces around {set temp}, because we're passing that
    literal script to the GUI, rather than a script returned by a call to
    [push] or [enter], for example. Note also the implicit FAIL if the
    verify condition isn't satisfied.


  • Next message: Glenn Jackman: "Re: How to count the repeating elements in a set of data"
    Loading