Sometimes it is necessary to have a timeout when verifying that a particular condition has been satisfied or the application has reached a particular state. For example, some UI tests may involve asynchronous operations or time-consuming tasks.
The following code listing shows how to perform assertions using timeouts. In our example, we are going to
assume that a JButton
is enabled after calling a web service asynchronously. In our example,
if the JButton
is not enabled after 10 seconds, the test will fail. The exception thrown
will include the description of the Condition
.
// import static org.assertj.swing.timing.Pause.pause;
// import static org.assertj.swing.timing.Timeout.timeout;
// import static org.assertj.swing.edt.GuiActionRunner.execute;
final JButton okButton = //obtain a reference to the "OK" button
pause(new Condition("OK button to be enabled") {
public boolean test() {
return execute(okButton::isEnabled);
}
}, timeout(10000));
A component formatter is an implementation of the interface ComponentFormatter
that creates a
String
representation of a GUI component. AssertJ Swing provides default component
formatters for all the Swing components in the JDK. Unlike the toString()
method in Swing
components, the provided component formatters display only the information that can help developers solve
problems in functional tests, excluding any information related to the appearance of GUI components
(e.g. colors, layouts, sizes, etc.). Here are some examples:
org.assertj.swing.test.TestFrame[name='frame', title='FormattingTest', enabled=true, showing=true]
javax.swing.JButton[name='button', text='A button', enabled=false]
javax.swing.JList[name='list', selectedValues=['One', 2], contents=['One', 2, 'Three', 4], selectionMode=MULTIPLE_INTERVAL_SELECTION, enabled=true]
javax.swing.JOptionPane[message='A message', messageType=ERROR_MESSAGE, optionType=DEFAULT_OPTION, enabled=true, showing=false]
javax.swing.JTabbedPane[name='tabbedPane', selectedTabIndex=1, selectedTabTitle='Second', tabCount=2, tabTitles=['First', 'Second'], enabled=true]:
There might be cases that you might want to create your own custom formatter to override an existing one or to add support for custom GUI components.
The interface ComponentFormatter
provides two methods:
Class<? extends Component> targetType()
returns the type of component this formatter
supports. For example, by returning JButton.class
a formatter indicates that it supports
instances of JButton
and its subclasses.String format(Component c)
returns the String
representation of the given GUI
component.The easiest way to create a component formatter is to configure an instance of
IntrospectionComponentFormatter
, which, as the name suggests, uses introspection to display
property values of a GUI component. The following code listing shows how to configure a
IntrospectionComponentFormatter
to support JLabel
(and subclasses) and the
properties to show:
IntrospectionComponentFormatter formatter =
new IntrospectionComponentFormatter(JLabel.class, "name", "text", "enabled");
After creating a custom formatter, we need to register it with AssertJ Swing. It is very simple, we
only need to call the static method register
in
org.assertj.swing.format.Formatting
.
Formatting.register(new MyFormatter());
formatter1
, a formatter registered to format instances of
JButton
, and formatter2
, a formatter registered to format instances of
MyOwnButton
(a subclass of JButton
), AssertJ Swing will use
formatter2
to format instances of MyOwnButton
.
It is very likely that, in your application, you are using custom Swing components (e.g. Flamingo, JIDE, or your own). Since AssertJ Swing currently supports only the standard Swing components (the ones that come in the JDK), you might want to create your own AssertJ Swing fixtures to test your application.
The following are some suggestions or recommendations you can follow when creating your own AssertJ Swing fixture:
By reading AssertJ Swing's code you can learn to use the BasicRobot
to simulate a user
moving the mouse, clicking a mouse button or pressing keyboard keys. We have separated the structure of a
component fixture in several layers (from bottom to top):
BasicRobot
. Simulates a user interacting with a mouse and keyboard. It uses the AWT Robot to
generate native input events.JComboBoxDriver
knows how to simulate a user using a JComboBox
(selecting a particular element) and how to verify the state of it (which element should be
selected).If the component you want to test is a subclass of a JDK Swing component (e.g. you have a custom button that
extends JButton
), you can extend an existing concrete AssertJ Swing fixture
(JButtonFixture
in our example).
If the custom GUI component does not extend any JDK Swing component, or if you prefer to create a AssertJ Swing fixture from scratch, please read the following
Robot
. It also
provides some very basic functionality and convenience methods.JPopupMenu
,
extend AbstractJPopupMenuInvokerFixture.By default, implementations of AbstractContainerFixture
provide shortcut methods to access the standard Swing components (the ones that come in the JDK) in a Container. For example, the following code listing
shows shortcuts methods to access a JLabel
and a JTree
from a JFrame
being managed by a FrameFixture
:
FrameFixture frame = new FrameFixture(new MyFrame());
frame.label("pathLabel").requireText("Path:");
frame.tree("navigationTree").selectPath("c:/projects/fest");
If you have created a fixture for your custom GUI component, it is not possible to add a shortcut to
AbstractContainerFixture
due to the lack of extension methods in Java. For example, let's assume you have
created a custom GUI component called MyCalendar
and a fixture to use this custom component in
your GUI tests called MyCalendarFixture
. It is not possible to add the shortcut method calendar
to AbstractContainerFixture
and have all its implementations look like this:
frame.calendar("myCalendar").selectDate("10/18/08");
To overcome this limitation, AssertJ Swing provides ComponentFixtureExtension.
The following code listing shows an extension to add a shortcut to a MyCalendarFixture
. This extension looks up a
MyCalendar
that has a matching name and is showing on the screen:
public class MyCalendarFixtureExtension extends ComponentFixtureExtension {
public static MyCalendarFixtureExtension calendarWithName(String name) {
return new MyCalendarFixtureExtension(name);
}
private final String name;
private MyCalendarFixtureExtension(String name) {
this.name = name;
}
public MyCalendarFixture createFixture(Robot robot, Container root) {
MyCalendar calendar = robot.finder().findByName(root, name, MyCalendar.class, true);
return new MyCalendarFixture(robot, calendar);
}
}
The only method that needs to be implemented is createFixture(Robot, Container)
, which is
responsible for creating our custom fixture. The static method calendarWithName(String)
is just
a convenience factory method, which we will use to connect our extension to any implementation of
AbstractContainerFixture
:
// import static org.assertj.swing.sample.MyCalendarFixtureExtension.calendarWithName;
FrameFixture frame = new FrameFixture(new MyFrame());
frame.with(calendarWithName("myCalendar")).selectDate("10/18/08");
A common topic in the mailing list is how to handle System.exit
. The typical example is
simulating a user selecting the menus File, Exit from a GUI test. By doing this, the
application will quit and any GUI test trying to use the application will fail.
To prevent this failure and to test that in fact System.exit
is being handled correctly by the
application, AssertJ Swing provides a NoExitSecurityManager
that will prevent the
application to end. To use it, simply add a call to NoExitSecurityManagerInstaller
as
follows:
private static NoExitSecurityManagerInstaller noExitSecurityManagerInstaller;
@BeforeClass
public static void setUpOnce() {
noExitSecurityManagerInstaller = NoExitSecurityManagerInstaller.install();
}
@AfterClass
public static void tearDown() {
noExitSecurityManagerInstaller.uninstall();
}
By default, NoExitSecurityManager
will just prevent the application from quitting if
System.exit
is called. If you want to do something when System.exit
is called, do
the following:
ExitHook
ExitHook
to
NoExitSecurityManagerInstaller.install(ExitHook)
Sometimes, when testing our applications, we need to deal with platform-specific issues.
AssertJ Swing provides the utility class Platform
to overcome this type of issues.
Currently Platform
provides one method, controlOrCommandKey()
that returns the
code of either the Command key (if MacOS) or the code of the Control key (if other than
MacOS, for example Windows). A good example of the utility of this method is simulation of a user copying
some text to the clipboard. In windows we need to simulate a user pressing the keys Ctrl and
C, while in MacOS we need to simulate a user pressing the keys Cmd and
C. With AssertJ Swing we can simply write:
// import static org.assertj.swing.util.Platform.controlOrCommandKey;
// import static java.awt.event.KeyEvent.VK_C;
int controlOrCommand = controlOrCommandKey();
dialog.textBox("username").selectAll()
.pressKey(controlOrCommand)
.pressAndReleaseKeys(VK_C)
.releaseKey(controlOrCommand);
AssertJ Swing provides the utility class Containers
to help test a Container
(e.g. a JPanel
) without having a Frame
(or JFrame
). The following
code listing shows how to test our own JPanel
of type MyCoolJPanel
:
// import static org.assertj.swing.fixture.Containers.showInFrame;
private FrameFixture frameFixture;
@Before
public void setUp() {
MyCoolJPanel panel = createPanel() // create panel in EDT.
frameFixture = showInFrame(panel);
}
AssertJ Swing will show the panel in a JFrame
, wrapped in a FrameFixture
.