Dedicated to Ashley & Iris




НазваниеDedicated to Ashley & Iris
страница9/71
Дата24.09.2012
Размер3.51 Mb.
ТипДокументы
1   ...   5   6   7   8   9   10   11   12   ...   71

Regulate the Event Queue


To take control of a window, under test, you must learn how its event queue dispatches messages. Some messages you must throttle—especially the paint messages. And some messages you must let through, such as a test case’s messages that simulate user input.

As test cases learn to control these aspects, they will grow tools to display windows under test. Write a function called reveal(), and call it from test cases, to obtain the next two Principles:

Temporary Visual Inspections


Turn on the Event Queue, during a test case, to visually inspect its window, populated by test data. The test run shall block until the window closes, then subsequent tests shall run.

Temporary Interactive Tests


While that GUI displays, drive it manually to research the GUI’s behavior.

Those two temporary practices provide an alternative, test-side GUI. The reveal() fixture displays the window under test, populated with test data. That allows us to spot-check our test case, and adjust the test-side support for the other practices.

Then we comment out the reveal() call, and ensure the test code works without visual inspections.

The ability to temporarily reveal() leads to a convenient platform to record images of windows under test.

Broadband Feedback


GUIs can record animations of activities during tests. Acceptance Test Frameworks can command test cases to record a gallery of results.

The previous 6 Principles force the review of GUI appearance and behavior into the tests, away from the wizards, form painters, and finished applications. They allow us to take control of the situation, and prevent the need to excessively manually test.

Now we leverage those Principles to develop test fixtures (test-side reusable methods) that enable predictable changes.

Query Visual Appearance


GUI controls often provide the ability to query back their display values. All such queries are simulations. Some results aren’t accurate! Tests must learn how to query that a GUI correctly obeyed the commands from its GUI Layer code.


  • Tests on Controls may Get details which Production code Set.

  • Tests on Scripts parse the script, looking for details.

  • Tests on Paint() events use Mock Graphics objects to generate a Log String.


If a Temporary Visual Inspection reveals some difference of opinion between your assertions and your actual GUI, you need to research how to query more accurately. Invest this research into test fixtures that you can then re-use.

Simulate User Input


Test cases simulate events and check responses. This is harder than it sounds, because GUI Toolkits always treat signals from their programmer-side differently from their user-side.

The fix, generally, is to write a Temporary Interactive Test that investigates differences between the GUI Toolkit’s available systems and a real user’s input. Then new test fixtures can encapsulate these differences, making high-level tests easier to write.

Loose User Simulations


Call the same method as an event handler would have called.

Most of our Case Studies rely on this technique as it is low risk. Sometimes only simple logic binds a message, such as the targets of a WTL BEGIN_MSG_MAP() macro (see page 205). In these systems, the binding code is structure, not behavior. If you won’t refactor BEGIN_MSG_MAP()’s innards, you have fewer reasons to strictly test that a message of type IDOK will indeed travel through the message queue and arrive at OnCancel().

If an ‘if’ statement, or a more complex expression, binds the event, then you must test this behavior.

Firm User Simulations


Write a test fixture that finds the handler bound to an event and calls it.

All GUI Toolkits come with the risk that they can Set a control’s value, such as its bound event handler, without providing a matching Get to robustly query that handler. A GUI Toolkit written to support test-free projects does not understand why your code might try to Get the same value that your code just Set.

This situation may require extra research to learn just how to accomplish that tricky Get. The Tk library, for example, allows us to query a control’s internal data structure (see page 122 for this technique). Our code must then navigate that structure to find the bound method reference within. And this code must still work when details that toolkit maintainers consider private change.

Coupling our tests to a GUI Toolkit’s internal systems introduces the risk that our vendors may upgrade that system and break our tests. These and other risks may push our tests up this list towards Soft User Simulations, or down into the territory of capture/playback tools.

Strict User Simulations


Push a raw input event into the event queue.

If you research your GUI Toolkit’s event queue, and learn its exact operation, you can often uncover its mechanism to forward raw inputs. Then you build fake event messages, push them into the event queue, and operate the queue so its other end dispatches events. Invest this research, as usual, into test fixtures that enable the kinds of fake input events that test cases for your application require.

This is how generic capture/playback tools test GUIs; by intercepting input events at the OS layer of the GUI Toolkit, and then by simulating these events in large batches. This “nuclear option” of GUI Testing should only be used after exploring the alternatives that permit test-first programming. You can't use capture/playback to test first.

No Case Study in this book needs such a high level of simulation. The following example code uses a rare and exotic language called “Java”, invented by James Gosling. The example reveals a GUI test rig called “Jemmy”, invented by Aleandre Iline. It exercises complete Event Queue Regulation for the Swing GUI Toolkit. The small penalty for these easy features is windows that flicker and animate their behaviors during tests.

Special thanks to Andrew de Torres for presenting this to the XP San Diego Users Group, and for allowing me to use it here.

The target of this test is a simple Swing form:


package src.com.ureach.detorres.jemmytest;

import java.awt.GridLayout;

import java.awt.event.*;

import javax.swing.*;


/* This simple “Hello World” application demonstrates testing Swing applications with JUnit (by Kent Beck and Erich Gamma) and Jemmy. The application consists of a JFrame with a JTextField, JButton, and JLabel. Initially the field is blank, the button is disabled, and the label is blank. When you type “Enable button” into the field, the button enables. When you push the button, a dialog displays. When you acknowledge the dialog, the label displays a status message, and the field and button are disabled.


* @author Andrew de Torres, detorres@ureach.com

*/


public class HelloWorld

{


private JTextField textfield;


private JButton button;


private JLabel label;


public HelloWorld()

{

textfield = new JTextField(20);

button = new JButton("Push me");

button.setEnabled(false);


label = new JLabel();


JPanel panel = new JPanel(new GridLayout(3, 1));

panel.add(textfield);

panel.add(button);

panel.add(label);


final JFrame frame = new JFrame("Hello, World!");

frame.setContentPane(panel);


textfield.addKeyListener(new KeyAdapter() {


public void keyReleased(final KeyEvent evt) {

if (textfield.getText().equals("Enable button")) {

button.setEnabled(true);

} else {

button.setEnabled(false);

}

}

});


button.addActionListener(new ActionListener() {

public void actionPerformed(final ActionEvent evt) {

JOptionPane.showMessageDialog(frame,

"You're almost done.",

"Hi!", JOptionPane.INFORMATION_MESSAGE);

label.setText("You're done.");

textfield.setEnabled(false);

button.setEnabled(false);

}

});


frame.pack();

frame.setLocationRelativeTo(null);

frame.show();

}


public static void main(String[] args) {

new HelloWorld();

}

}


// eof "HelloWorld.java"


Jemmy solves a common problem simulating events. Suppose a button provides a .push() method, so production code can simulate user input. However, suppose that .push() method worked even if its button were disabled. When test cases rely on the .push() method, alone, its results might mislead. If a button was disabled and should be enabled, then if a test called .push() and recorded an enabled response, the test would not catch that bug.

This test uses wrappers, called Observers, which match simulated input behaviors to real input behaviors:


// HelloWorldTestUI.java


package test.com.ureach.detorres.jemmytest;

import java.awt.event.WindowAdapter;

import java.awt.event.WindowEvent;

import javax.swing.WindowConstants;

import junit.framework.Test;

import junit.framework.TestCase;

import junit.framework.TestSuite;

import org.netbeans.jemmy.*;

import org.netbeans.jemmy.operators.*;

import src.com.ureach.detorres.jemmytest.HelloWorld;


/* JUnit/Jemmy test for HelloWorld. This class demonstrates the basics of testing a Swing application using Jemmy inside of JUnit.


* @author Andrew de Torres, detorres@ureach.com

*

*/


public class HelloWorldTestUI

extends TestCase

{

public HelloWorldTestUI(String testName) {

super(testName);

}


class Flag { boolean flag; }

final Flag flag = new Flag();


public void reveal(final JFrameOperator frame)

throws InterruptedException

{

frame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);


frame.addWindowListener(new WindowAdapter() {

public void windowClosing(WindowEvent e) {

flag.flag = true;

frame.removeWindowListener(this);

}

});


while (!flag.flag)

Thread.sleep(100);


} // by Timothy Wall


/**

* Our single test method which tests everything.

*/

public void testIt() throws InterruptedException

{

// Turn off Jemmy output.

JemmyProperties.setCurrentOutput(TestOut.getNullOutput());


/* Because Jemmy operates a real event queue, it must await streams of messages to communicate between the platform and the GUI Toolkit. Some controls won’t finish painting until after an indefinite number of messages, so Jemmy relies on small timeouts. For each kind of control, test cases must declare how long they will wait before grabbing that control. A complete test rig would configure these defaults in a fixture such as setUp().

*/

// Shorten timeouts so things fail quicker.

Timeouts.setDefault("FrameWaiter.WaitFrameTimeout", 5000);

Timeouts.setDefault("DialogWaiter.WaitDialogTimeout", 5000);

Timeouts.setDefault("ComponentOperator.WaitComponentTimeout", 5000);


// Start the application under test.

HelloWorld.main(new String[0]);

/* Jemmy wraps each kind of target control in an Operator object. This test fixture mediates between test cases and simulated user inputs.

*/


final JFrameOperator frame = new JFrameOperator(

"Hello, World!");


// Find 1st (0th) JTextField in frame.

JTextFieldOperator textfield = new JTextFieldOperator(frame, 0);

assertTrue("textfield is enabled", textfield.isEnabled());

assertTrue("textfield is editable", textfield.isEditable());

// reveal(frame);


// Find "Push me" button.

JButtonOperator button = new JButtonOperator(frame, "Push me");

assertTrue("button is disabled", !button.isEnabled());

JLabelOperator label = new JLabelOperator(frame, 0);

assertEquals("label is blank", "", label.getText());

/* If this button.push() were not commented out, it would do nothing. Jemmy Operators wrap each kind of target control in an Operator object. This test fixture mediates between test cases and simulated user inputs.

*/


// button.push();


// Simulate typing text.

textfield.typeText("Enable button");

assertTrue("button is enabled", button.isEnabled());

// Simulate pushing button.

button.push();


// Note partial match - dialog title is "Hi!".

JDialogOperator dialog = new JDialogOperator("Hi");

new JButtonOperator(dialog, "OK").push();

assertEquals("label changed", "You're done.", label.getText());

assertTrue("textfield is disabled", !textfield.isEnabled());

assertTrue("button is disabled", !button.isEnabled());


// Throw in a dispose here incase we run with

// the JUnit GUI (see main method).

frame.dispose();

}


public static Test suite()

{

TestSuite suite = new TestSuite(HelloWorldTestUI.class);

return suite;

}

/* The main() function runs the test using the JUnit test runner. If the single argument “swing” is specified, the GUI test runner is used. Otherwise, the command line test runner is used.

* @param _args command line args: ["swing"]

*/

public static void main(String[] _args)

{

String[] testCaseName =

{ HelloWorldTestUI.class.getName() };


// _args[0] = "swing";


if (_args.length == 1 && _args[0].equals("swing")) {

junit.swingui.TestRunner.main(testCaseName);

} else {

junit.textui.TestRunner.main(testCaseName);

}

}

}


// eof "HelloWorldTestUI.java"


An off-the-shelf GUI test rig should provide a balanced set of generic fixtures that cover common GUI aspects. Even so, our case testIt() is very long. As we add more tests to this project, Extract Method Refactor will merge code out of long cases, and grow new, reusable, application-specific fixtures. New test cases, for this specific application, will become easier to write.

Ultimately, user simulations can replace a user with a Mock Object that generates a complete sequence of input events. Page 321 shows how to record a trace of user inputs (from a Temporary Interactive Test, naturally), and assemble them to form a “Motion Capture”.

If a Temporary Interactive Test reveals some difference of opinion between your simulations and your actual GUI, you need to research how to simulate more accurately. Invest this research into test fixtures that you can then re-use.

All that test infrastructure must interact with your production GUI, your test code, and your editor, in ways that rapidly identify the sources of errors.
1   ...   5   6   7   8   9   10   11   12   ...   71

Похожие:

Dedicated to Ashley & Iris iconDedicated to Ashley & Iris

Dedicated to Ashley & Iris iconAbramovitz, Janet N., and Ashley T. Mattoon. 1999

Dedicated to Ashley & Iris iconPlastics and the Environment. Hoboken. N. J. Wiley-Interscience. Ashley, S. 2002

Dedicated to Ashley & Iris iconGilliland home is dedicated

Dedicated to Ashley & Iris iconCitations Acknowledging or using iris-related facilities and Data As of August 2010 Please send corrections and/or additions to

Dedicated to Ashley & Iris iconDedicated to Jerry Lefcourt, Lawyer and Brother

Dedicated to Ashley & Iris iconFree to download magazine dedicated to Commodore computers

Dedicated to Ashley & Iris iconMorning session I: Dedicated to Prof. A. Acrivos, “Suspensions and particulates”

Dedicated to Ashley & Iris icon08: 30 Registration 09: 00 Welcome Remarks Morning session I: Dedicated to Prof. A. Acrivos, “Suspensions and particulates”

Dedicated to Ashley & Iris iconThe Culture of Irises in the United States Iris Culture for the Mountain and Plains Region, D. M. Andrews 5

Разместите кнопку на своём сайте:
Библиотека


База данных защищена авторским правом ©lib.znate.ru 2014
обратиться к администрации
Библиотека
Главная страница