13.12. How to Create Semi-Automatic Tests that Query for User Input

13.12.1. Querying the User using Qt
13.12.2. Querying the User using Java AWT/Swing

The most common use case for Squish tests is full automation—Squish is used to record a test (or we write a test manually), and then plays the test back and reports the results. But in some situations we might want to create a semi-automatic test that requires the tester to provide some input. For example, when testing some software with a hardware device we might want to ask the user who is running the test if the device's state has been changed in the expected way.

For example, imagine that we want to write tests for some printer-related software. The tests exercise the software, and one of the results should be that the printer prints a page with the text “This is a test”.

Since Squish can't verify a physical occurrence such as a page coming out of the printer with some specific text on it, any test that has such a requirement must rely on the tester to inform Squish whether the test passed or failed. The following example shows how this can be done.

[Warning]Toolkit-Specific

The examples in this section use the Qt and Java AWT/Swing toolkits, and so it is necessary that a Qt or Java AWT/Swing application is set as the AUT for the examples to run successfully. Furthermore, the examples depend on specific features of the toolkits themselves, so it is not possible to do similar things with other toolkits at this time. (Internally, froglogic is researching the possibility of providing a generic toolkit-agnostic message box facility to support querying for user input.)

13.12.1. Querying the User using Qt

Python

    messageBox = QMessageBox()
    if qVersion() >= "4.2.0":
        messageBox.setWindowTitle("Squish - Confirm Printing")
    messageBox.setText("Did the printer correctly print the test page?")
    messageBox.addButton(QMessageBox.Yes)
    messageBox.addButton(QMessageBox.No)
    which = getattr(messageBox, "exec")() # Can't write messageBox.exec() since 'exec' is reserved
    if which == QMessageBox.Yes:
        test.passes("Print Test", "Test page was correctly printed.")
    else:
        test.fail("Print Test",
                "Test page was incorrectly printed or not printed at all.")

JavaScript

    var messageBox = new QMessageBox();
    if (qVersion() >= "4.2.0")
        messageBox.setWindowTitle("Squish - Confirm Printing");
    messageBox.setText("Did the printer correctly print the test page?");
    messageBox.addButton(QMessageBox.Yes);
    messageBox.addButton(QMessageBox.No);
    var which = messageBox.exec();
    if (which == QMessageBox.Yes)
        test.pass("Print Test", "Test page was correctly printed.");
    else
        test.fail("Print Test",
                "Test page was incorrectly printed or not printed at all.");

Perl

    my $messageBox = QMessageBox->new();
    if (qVersion() ge "4.2.0") {
        $messageBox->setWindowTitle("Squish - Confirm Printing");
    }
    $messageBox->setText("Did the printer correctly print the test page?");
    $messageBox->addButton(QMessageBox::Yes);
    my $yesId = 16384;
    $messageBox->addButton(QMessageBox::No);
    my $which = $messageBox->exec();
    if ($which == $yesId) {
        test::pass("Print Test", "Test page was correctly printed.");
    } else {
        test::fail("Print Test",
                "Test page was incorrectly printed or not printed at all.");
    }

Tcl

    set messageBox [construct QMessageBox]
    if {[invoke qVersion] >= "4.2.0"} {
        invoke $messageBox setWindowTitle "Squish - Confirm Printing"
    }
    invoke $messageBox setText "Did the printer correctly print the test page?"
    invoke $messageBox addButton [enum QMessageBox Yes]
    invoke $messageBox addButton [enum QMessageBox No]
    set which [invoke $messageBox exec]
    if {$which == [enum QMessageBox Yes]} {
        test pass "Print Test" "Test page was correctly printed."
    } else {
        test fail "Print Test" \
                "Test page was incorrectly printed or not printed at all."
    }

Here we have created a QMessageBox, set its values appropriately to our needs and then call its exec method to pop it up. We then retrieve the user's response (a click of the Yes or No button), and call a suitable Squish test function. (Note that the window title is only available from Qt 4.2, so we only use the QMessageBox.setWindowTitle method if the version of Qt is high enough.)

We are not limited to using simple button-based dialogs if the underlying GUI toolkit supports more. For example, the Qt toolkit has a QInputDialog class that can be used to get integers or floating-point values, or a string from a predefined list of strings.

Python

    pages = QInputDialog.getInteger(0, "Squish - Page Count",
            "How many pages were printed?")
    if pages == 0:
        test.fail("Page Count", "No pages printed.")
    else:
        test.passes("Page Count", "%d pages printed." % pages)

JavaScript

    var pages = QInputDialog.getInteger(0, "Squish - Page Count",
            "How many pages were printed?");
    if (pages == 0)
        test.fail("Page Count", "No pages printed.");
    else
        test.pass("Page Count", pages + " pages printed.");

Perl

    my $pages = QInputDialog::getInteger(0, "Squish - Page Count",
            "How many pages were printed?");
    if ($pages == 0) {
        test::fail("Page Count", "No pages printed.");
    } else {
        test::pass("Page Count", "$pages pages printed.");
    }

Tcl

    set pages [invoke QInputDialog getInteger 0 "Squish - Page Count" \
            "How many pages were printed?"]
    if {$pages == 0} {
        test fail "Page Count" "No pages printed."
    } else {
        test pass "Page Count" "$pages pages printed."
    }

Here we ask the user to enter the number of pages. The static QInputDialog.getInteger function's first argument is the parent window for which we pass 0 as a safe default. The second argument is the dialog's window title, and the third argument the dialog's body text.

This section again shows that using Squish's powerful script bindings to classes like QMessageBox and QInputDialog (or to the DOM/HTML, Tk, XView, or other APIs, depending on which Squish edition is being used), can be used in test scripts so that testers can be asked for input when necessary. However, asking for tester input is something to be avoided where possible, since fully automated tests are much faster and more convenient.

13.12.2. Querying the User using Java AWT/Swing

Python

    AnyClass = jtable.getClass()
    OptionPaneClass = AnyClass.forName("javax.swing.JOptionPane")
    optionPane = OptionPaneClass.newInstance()
    reply = optionPane.showConfirmDialog(0, "Were 3 addresses printed?",
        "Print check", optionPane.YES_NO_OPTION)
    if reply == optionPane.YES_OPTION:
        test.passes("Printed successfully")
    else:
        test.fail("Printing failed")

JavaScript

    var AnyClass = jtable.getClass();
    var OptionPaneClass = AnyClass.forName("javax.swing.JOptionPane");
    var optionPane = OptionPaneClass.newInstance();
    var reply = optionPane.showConfirmDialog(0,
            "Were 3 addresses printed?", "Print check",
            optionPane.YES_NO_OPTION);
    if (reply == optionPane.YES_OPTION)
        test.pass("Printed successfully");
    else
        test.fail("Printing failed");

Tcl

    set AnyClass [invoke $jtable getClass]
    set OptionPaneClass [invoke $AnyClass forName \
        "javax.swing.JOptionPane"]
    set optionPane [invoke $OptionPaneClass newInstance]
    set reply [invoke $optionPane showConfirmDialog \
        0 "Were 3 addresses printed?" "Print check" \
        [property get $optionPane YES_NO_OPTION]]
    if {$reply == [property get $optionPane YES_OPTION]} {
        test pass "Printed successfully"
    } else {
        test fail "Printing failed"
    }

We want to prompt the user to tell us whether or not printing was successful, and for that we need to use a confirmation dialog. The first three lines of the code take a Java object (any object will do) to get that object's class object. We then use the forName method to retrieve the class object for the class of object we want, and then use that class object's newInstance method to get an instance that we can then use. (See Wrapping custom Java™ classes (Section 15.3.7).)

In this case we get an optionPane instance and call its showConfirmDialog method; naturally we could use any of the other dialog methods if we wished.