Testing Eclipse GUIs still is an elaborate and extensive task. There are two ways to get the task done: Record-and-play and programming by hand. Test-first programmers usually prefer to program their tests by hand instead of using record-and-play tools, which are typically used by test departments that construct large functional test suites.
When programming UI tests by hand, the programmer is let alone mainly with SWT Robots. While they are great tools to find and use widgets, they naturally can't find their controllers like the the JFace Viewers and the Workbench ViewParts. In test-first development this becomes a problem because the programming level for test and productive code is at a different abstraction level.
Matthias Kempka has found a way to implement a JFace/Workbench Robot. It finds JFace Viewers for SWT elements and eases the testing with views and editors. Declarative UI Elements become accessible for programmed tests. In his talk Matthias Kempka shows how he uses the JFace/Workbench Robot to develop GUI components in Eclipse test-driven and gives an introduction to the key ideas that make the Robot work.
3. Creating code test-driven
TDD creates little pieces of code
Readable
Simple
Flexible
Regression tests already there
Great, uh?
Unit Tests
Piece of Code
4. Where should I put it?
A framework has many hooks
Did I hook up my PoC in the right spot?
Did I hook it up at all?
Eclipse Framework
Piece of Code
5. „Testing“ by hand
Easiest approach: Try it.
Regression tests?
*click*
Developer
use
Eclipse Framework
Piece of Code
6. PDE Tests
Let's write regression tests
PDE tests can start our test code with a workbench
Some Eclipse functionality is hard to reach programmatically
PDE Unit Tests
Eclipse Framework
Piece of Code
7. Functional Tests with SWT Robots
SWT Robots can simulate User interaction
Is it easier to find a ViewAction button on the screen than in the Workbench
API?
PDE Unit Tests
use SWT *click*
Robot
use
Eclipse Framework
Piece of Code
8. Are SWT Robots the solution?
Can find SWT widgets in the widget hierarchy
Provides convenience methods to use the widgets
Can read feedback from the UI
Display
Shell
Composite
Label Text
9. What's not in the Widget Hierarchy?
Basically all objects an Eclipse programmer touches are not in the widget
hierarchy
Association with IDs from plugin.xml is lost
Display IViewPart IActionDelegate
Shell IMarker
IEditorPart
Composite TreeViewer
IHandler
Label Text TableViewer
...
10. Functional Tests with Robots
A JFace/Workbench Robot eases access to the interesting parts of the UI.
Provides access to JFace controllers
SWT Robot *click*
PDE Unit Tests
JFace/Workbench
Robot
use
Eclipse Framework
Piece of Code
11. Sammy: The Mission
Make common things easy, make difficult things possible
I need to set up test data for the TableViewer behind this SWT Table on the
screen? How can I reach it?
I'm writing a view. How can I test that all the listeners are attached correctly?
I'm writing a contribution to a view (i.e. a ViewAction) – How can I test that
my entries in plugin.xml works?
Does my LabelProvider return the right image?
12. Writing a test with JFace/Workbench Robot
Sammy
Write a test case:
Set up: Prepare a view for the actual test
– open the view
Tear down:
– Clean up allocated resources: Close the view
Actual test
– Find the TableViewer in that view and set a test-specific input
– Check that a view action is not enabled
– Select an item in the table
– Check that the view action is enabled
13. Set up: Open a view
Views could be opened using IWorkbenchPage.showView( id )
– Too cumbersome for such a common use case
– No immediate feedback on errors
Sammy.openView( id ) eases setting up test cases
– easy accessible API
– Show errors in red test case instead of UI
– Remembers the view for cleaning up
public void setup() throws Exception {
sammy = new Sammy();
sammy.openView( ViewUnderTest.ID );
}
14. Tear down
Instances of Sammy (and SammyIDE) remember the resources they
allocated and can release (close or delete) them
– IViewPart
– IEditorPart
– IResource
public void teardown() throws Exception {
sammy.cleanup();
}
15. Test: Finding the view
Every supported JFace/Workbench Element has an Operator:
– IViewPart – ViewOperator
– IEditorPart – EditorOperator
– TableViewer – TableViewerOperator
– …
The Operator gives access to the described JFace/Workbench element
– Convenience methods for common use cases
Operator constructor
– takes parameters that describe the JFace/Workbench element
– returns without Exception only if the described element was found
– waits a time for the element to appear
public void testViewAction() throws Exception {
ViewOperator vo = new ViewOperator( “View Title” );
}
16. Test: Finding the TableViewer
Operators for workbench parts know their parent composite
– Given in IWorkbenchPart.createPartControl( Composite )
Operators for JFace elements
– take a parent of the SWT element or the SWT element itself
– can associate the SWT element with the JFace abstraction
public void testViewAction() throws Exception {
ViewOperator vo = new ViewOperator( “View Title” );
TableViewerOperator tvo = new TableViewerOperator(
vo.getParentComposite() );
}
17. Test: Setting the Input
Setting up Test Data often includes setting a special input on a viewer
Having the TableViewer already available eases this remarkably
*Operator.getSource()returns the actual JFace/Workbench element
public void testViewAction() throws Exception {
ViewOperator vo = new ViewOperator( “View Title” );
TableViewerOperator tvo = new TableViewerOperator(
vo.getParentComposite() );
tvo.getSource().setInput( new String[] { “a” } );
}
18. Test: Checking ViewerOperator enablement
A ViewActionDelegate only is instanciated once the user selects the
ViewAction
The ViewActionOperator knows the Proxy by the workbench as well as
the delegate
public void testViewAction() throws Exception {
ViewOperator vo = new ViewOperator( “View Title” );
TableViewerOperator tvo = new TableViewerOperator(
vo.getParentComposite() );
tvo.getSource().setInput( new String[] { “a” } );
ViewActionOperator vao = new ViewActionOperator( vo,
MyViewAction.ID );
assertFalse( vao.isEnabled() );
}
19. Test: Checking ViewerOperator enablement
Now all the elements are in place to make this test useful
– The View shows up (layout is not tested)
– XML Code in plugin.xml is tested
– Makes sure that the TableViewer is a SelectionProvider
public void testViewAction() throws Exception {
ViewOperator vo = new ViewOperator( “View Title” );
TableViewerOperator tvo = new TableViewerOperator(
vo.getParentComposite() );
tvo.getSource().setInput( new String[] { “a” } );
ViewActionOperator vao = new ViewActionOperator( vo,
MyViewAction.ID );
assertFalse( vao.isEnabled() );
tvo.getSource().setSelection( new StructuredSelection(“a”));
assertTrue( vao.isEnabled() );
}
20. Sammy Internals
For many things, Sammy relies on mapping from widgets to
JFace/Workbench elements
– And vice versa
Some widgets are nowhere accessible in the workbench
– Where necessary aspects are used to trap widget instances
For some things, Sammy has to access internals
– i.e. the View that shows an error holds it in a private field
– This leads to compatibility layers, but at least it's in one place
Widget Viewer
21. Sammy Future
More than proof of concept, less than beta
A good state to start collecting requirements
http://www.innoopract.com/en/developers/mkempka/sammy