Simplifying Development and Testing of GUIs with SAF (JSR 296) and FEST Michael Hüttermann Training & Consulting Alex Ruiz Oracle Corporation
Agenda Why do we need a Swing framework? Introducing the Swing Application Framework (SAF) SAF Features Status of SAF Testing Swing UIs Why UI testing is hard Writing testable UIs Introducing FEST FEST Features FEST Roadmap
About us Alex Ruiz Senior Engineer at Oracle Founder and major contributor of FEST Father ;-) Michael Hüttermann Self-employed consultant on Java TM /JEE, SCM/ALM, SDLC tooling and agile development Speaker at conferences, author of books and articles Stage producer of Agile 2009's Tools for Agility track
Agenda Why do we need a Swing framework? Introducing the Swing Application Framework (SAF) SAF Features Status of SAF Testing Swing UIs Why UI testing is hard Writing testable UIs Introducing FEST FEST Features FEST Roadmap
Why do we need a Swing framework? Swing: powerful but difficult Swing has been available for many years It is very powerful and very complex Too easy to do things wrong and get away with it Too many possible paths: scares beginners Developers should focus on the application domain, not on the architecture Need a standard way to develop Swing UIs All bigger projects do write an own framework
Why do we need a Swing framework? Swing is too difficult for beginners Easy to adopt bad practices: application is a JFrame Internationalization Performance Organization of event listeners Much boiler plate code Development is error prone Patterns and architecture do matter!
Why do we need a Swing framework? Even experienced developers can get it wrong The most common: violations of Event Dispatch Thread (EDT) access Incorrect division of responsibilities: classes that perform logic are event listeners at the same time Pretty long kick-off time, not "straight ahead" Why only web frameworks? Many more!
Agenda Why do we need a Swing framework? Introducing the Swing Application Framework (SAF) SAF Features Status of SAF Testing Swing UIs Why UI testing is hard Writing testable UIs Introducing FEST FEST Features FEST Roadmap
Introducing the Swing Application Framework (SAF) Provides a simple application framework for Swing applications Not intended to cover all needs of applications JSR 296 Reference implementation in java.net To be included in Java 7 (or not?) Not intended to compete with Netbeans RCP or Eclipse RCP Easy to learn: no more than a day
Agenda Why do we need a Swing framework? Introducing the Swing Application Framework (SAF) SAF Features Status of SAF Testing Swing UIs Why UI testing hard Writing testable UIs Introducing FEST FEST Features FEST Roadmap
SAF Features Application Lifecycle and common services Defines application lifecycle: startup, shutdown, etc. EDT-safe: o Application constructed in EDT o Application started in EDT Startup progress reported by standard milestones UI component (event) handling Basic session persistance Threading
SAF Features Application is not a JFrame anymore Developers only need to extend Application or a subclass, and override appropriate lifecycle methods: class MyApp extends Application { protected void startup(string[] args) { JFrame frame = new JFrame("My App"); frame.add(new JLabel("Hello World!")); frame.pack(); frame.setvisible(true); } public static void main(string[] args) { Application.launch(MyApp.class, args); } }
SAF Features Application is not a JFrame anymore Extend SingleFrameApplication for an application with a primary JFrame: class MyApp extends SingleFrameApplication { protected void startup(string[] args) { JPanel panel = new JPanel(new BorderLayout()); panel.add(acomponent, BorderLayout.NORTH); show(panel); } public static void main(string[] args) { Application.launch(MyApp.class, args); } }
SAF Features Component Resource Injection Set component properties whose names match a resource name Inject just one component, or all components in a hierarchy Resource names must match: component.getname() +.propertyname Converts Strings to correct type (e.g. icon) Uses good old ResourceBundle (*.properties files)
SAF Features Field Resource Injection Icon valued fields initialized from a ResourceMap: public class Form extends JPanel { @Resource Icon busyicon; @Resource Icon readyicon; void showstatus(boolean busy) { mylabel.seticon(busy? busyicon : readyicon); } Form() { Application app = Application.getInstance(); ResourceMap r = app.getcontext().getresourcemap(form.class); r.injectfields(this); } } ResourceBundle properties File: # resources/form.properties ResourceBundle Form.busyIcon = busy-icon.png Form.readyIcon = ready-icon.png
SAF Features Actions Plain old Swing world: o Actions implement Action or extend AbstractAction o Semantics: actionperformed(), enabled, selected o ActionMap maps names to actions
SAF Features Actions (II) With SAF: o Actions are simply annotated with @Action, one per actionperformed o They can also be asynchronous or block facets o One class can define many actions class MyActions { @Action void newfile() {... } @Action void fileopened(actionevent e) {... } }
SAF Features Actions (III) Asynchronous actions return a Task @Action Task<?,?> savefile() { return new MyTask(); } Task extends SwingWorker Overwrite methods like doinbackground()
SAF Features Actions (IV) Blocking actions have five scopes: NONE, ACTION, WINDOW, COMPONENT, APPLICATION @Action(block=BlockingScope.APPLICATION) Blocks the facet Provides basic functionality like blocking dialogs
SAF Features Action Maps Create an Action for each @Action: ActionMap amap = getcontext().getactionmap() cutbutton.setaction(amap.get("cut")); copybutton.setaction(amap.get("copy")); Action attributes come from a ResourceMap: # resources/myactions.properties ResourceBundle cut.action.text = Cut cut.action.accelerator = control U cut.action.shortdescription = Cut to clipboard copy.action.text = Copy copy.action.acceleratorkey = control C copy.action.shortdescription = Copies to clipboard
Agenda Why do we need a Swing framework? Introducing the Swing Application Framework (SAF) SAF Features Status of SAF Testing Swing UIs Why UI testing is hard Writing testable UIs Introducing FEST FEST Features FEST Roadmap
Status of SAF JSR 296 marked as inactive Original project lead left Sun Project revived under new lead: Alexander Potochkin Project resumed activity, call for contribution Further direction not clear in all facets What should be content of SAF? Legacy apps vs. greenfield projects SAF to be included in Java 7 SAF probably not included in Java 7
Agenda Why do we need a Swing framework? Introducing the Swing Application Framework (SAF) SAF Features Status of SAF Testing Swing UIs Why UI testing is hard Writing testable UIs Introducing FEST FEST Features FEST Roadmap
Agenda Why do we need a Swing framework? Introducing the Swing Application Framework (SAF) SAF Features Status of SAF Testing Swing UIs Why UI testing is hard Writing testable UIs Introducing FEST FEST Features FEST Roadmap
Testing Swing UIs Why? During development: o Testing GUIs can make the entire system safer and more robust o Tests to specify the functionality o Test early > better design During acceptance testing (specify features) and integration/system testing During maintenance: o Code might be refactored frequently to improve design, and this code often encloses great portions of the user interface. o Having a solid test suite that includes GUI code can give us confidence that we are not inadvertently introducing bugs.
Agenda Why do we need a Swing framework? Introducing the Swing Application Framework (SAF) SAF Features Status of SAF Testing Swing UIs Why UI testing is hard Writing testable UIs Introducing FEST FEST Features FEST Roadmap
Why UI testing is hard Conventional unit testing is not suitable for testing UIs: UI components are usually composed of more than one class The room for potential interactions with a UI is huge Conventional test coverage is not enough to cover all user interaction scenarios Addressing and driving the controls Test isolation and reuse of tests CR is not enough
Agenda Why do we need a Swing framework? Introducing the Swing Application Framework (SAF) SAF Features Status of SAF Testing Swing UIs Why UI testing is hard Writing testable UIs Introducing FEST FEST Features FEST Roadmap
Writing testable GUIs Separate Model and View Use a unique name for GUI components Do not test default component behavior Concentrate on testing the expected behavior of your GUI Test isolation Mocking Do not only use CR-Tools!
Agenda Why do we need a Swing framework? Introducing the Swing Application Framework (SAF) SAF Features Status of SAF Testing Swing UIs Why UI testing is hard Writing testable UIs Introducing FEST FEST Features FEST Roadmap
Introducing FEST Open Source Library for UI Testing Supports functional Swing UI testing Website: http://fest.easytesting.org Its API provides a fluent interface Open Source project (Apache 2.0 license) Supports both TestNG and JUnit Simplifies troubleshooting UI test failures Hosted at CodeHaus
Introducing FEST Robust UI testing Simulates user input using native UI events Provides a reliable mechanism for finding GUI components Tolerates changes in o Component position o Component size o Layout
Agenda Why do we need a Swing framework? Introducing the Swing Application Framework (SAF) SAF Features Status of SAF Testing Swing UIs Why UI testing is hard Writing testable UIs Introducing FEST FEST Features FEST Roadmap
FEST Features Fluent Interface Compact, intuitive and readable API Makes it easy to write and maintain UI tests dialog.combobox("domain").select("users"); dialog.textbox("name").entertext("leia"); dialog.button("login").click(); dialog.optionpane().requireerrormessage();
FEST Features Reliable Component Lookup By default, uses component names to look up UI components dialog.button("ok").click(); It can also use component types and custom search criteria (e.g. testing legacy applications) GenericTypeMatcher<JButton> textmatcher = new GenericTypeMatcher<JButton>() { protected boolean ismatching(jbutton button) { return "OK".equals(button.getText()); } }; dialog.button(textmatcher).click();
FEST Features Test Violations of EDT Access EDT access violation is a common bad practice, even for experienced Swing developers FEST can verify that EDT access is correct Simply install: FailOnThreadViolationRepaintManager.install();
FEST Features Troubleshooting UI test failures Failures may be due to: o Environmental conditions o A GUI component could not be found o More than one GUI component satisfied the given search criteria o Programming defect
FEST Features Troubleshooting UI test failures (II) Failure due to environmental conditions o Typical example: scheduled anti-virus software scan starts in the middle of our test session o A screenshot of the desktop at the moment of failure can help us determine if an environmental condition was responsible for the failure
FEST Features Troubleshooting UI test failures (III) Failure due to environmental conditions o FEST can automatically embed a screenshot of the desktop in a HTML test report (TestNG or JUnit)! o Configuration is easy, short and reuses existing infrastructure
FEST Features Troubleshooting UI test failures (IV) Failure due to GUI component not found o For example, looking for a button with name ok in a dialog that does not contain such button! o Having access to the current component hierarchy can help us figure out why a component could not be found o FEST includes the current component hierarchy when throwing a ComponentLookupException
FEST Features Troubleshooting UI test failures (V) Failure due to more than one GUI component satisfies a lookup condition o For example, looking up a button with name ok in a dialog o We have accidentally named two buttons with the same name! o Once again, FEST assists us by providing the list of found components in the thrown ComponentLookupException
FEST Features JavaFX Support public void shouldupdatetextboxwithpressednumber() { calculator.swingbutton(withtext("8")).click(); calculator.textbox().requiretext("8"); calculator.swingbutton(withtext("6")).click(); calculator.textbox().requiretext("86"); } Work in progress
FEST Features Fluent Assertions String[] newhires = employees.newhiresnames(); assertthat(newhires).containsonly("r2-d2"); assertthat(yoda).isinstanceof(jedi.class).isequalto(foundjedi).isnotequalto(foundsith);
FEST Features DSL for Java Reflection // Regular Java Reflection: Method method = Names.class.getMethod("get", int.class); AccessController.doPrivileged( new PrivilegedAction<Void>() { public Void run() { method.setaccessible(true); return null; } }); String name = (String) method.invoke(names, 8); // FEST-Reflection: String name = method("get").withreturntype(string.class).withparametertypes(int.class).in(names).invoke(8);
FEST Features Template for EasyMock @Test public void shouldupdateemployee() { new EasyMockTemplate(mockEmployeeDao) { protected void expectations() { mockemployeedao.update(employee); } protected void codetotest() { employeebo.updateemployee(employee); } }.run(); }
Agenda Why do we need a Swing framework? Introducing the Swing Application Framework (SAF) SAF Features Status of SAF Testing Swing UIs Why UI testing is hard Writing testable UIs Introducing FEST FEST Features FEST Roadmap
FEST Roadmap Provide support for 3rd party Swing libraries: o Flamingo Component Suite o JIDE o SwingX Groovy-based DSL Support for JavaFX (in the works) Much more!
Agenda Why do we need a Swing framework? Introducing the Swing Application Framework (SAF) SAF Features Status of SAF Testing Swing UIs Why UI testing is hard Writing testable UIs Introducing FEST FEST Features FEST Roadmap
Summary Swing is a very powerful UI toolkit With power comes complexity SAF can simplify creation of Swing UIs SAF is a lean application framework Creation is only the beginning, we need to test UI testing is necessary for improving the quality of our applications FEST provides a tool that makes creation and maintenance of UI tests a breeze Test your UIs!
Michael Hüttermann michael@huettermann.net Alex Ruiz alex.ruiz.05@gmail.com