This document contains a presentation about testing the user interface (UI) aspects of Eclipse plug-ins. It discusses using JUnit and simple tools to test UI parts of an application. It provides various strategies for testing perspectives, views, editors, and interaction using mouse/keyboard events, widget manipulation, and command execution. It also covers parameterized tests and ensuring no error messages are logged.
SQL Database Design For Developers at php[tek] 2024
Test UI aspects of Eclipse plugins
1. Redistribution and other use of this material requires written permission from The RCP Company.
PR0031 - 2010-11-01
Test UI Aspects of Plug-ins
Testing the UI of an Eclipse plug-in is usually considered rather difficult and something that is
best left for specialized tools. Also, the test is often considered very fragile and is expected to
be updated whenever you make even the smallest change to your UI.
But that need not be so. By using a few rather simple tools and a few just as rules, you can test
all the UI parts of an application...
This presentation was made for Eclipse Summit Europe 2010.
2. PR0029 - 2010-05-31
2
About Me
Tonny Madsen, Founder and Owner of The RCP Company
20 years of experience in system development in major companies
9 years experience as the Systems Architect of an 20+ MLoC project
8 years of experience with Eclipse and Eclipse RCP
Solutions Member of the Eclipse Foundation
Chairman of Eclipse.dk
Extern Lecturer at IT-University on Model Driven Development and Domain
Specific Languages
Regular speaker at EclipseCon, Eclipse Summit, etc
3. PR0031 - 2010-11-01
3
Downloads
This presentation makes some use of a library of low-level test methods.
These can be downloaded from
http://rcp-company.com/uploads/materials/ESE-2010/TestPlugins-2010-11-01.zip
These slides are also available as
http://rcp-company.com/uploads/materials/ESE-2010/PR0031 - Eclipse Summit Europe '10 -
Test UI Aspects of Plug-ins.pdf
4. PR0031 - 2010-11-01
4
JUnit Support in Eclipse
Both JUnit 3 and 4 are supported in Eclipse 3.4
Alternatively use TPTP Testing framework
Special Launch types for “JUnit” and “JUnit Plug-in Test”
Special JUnit view
Can create new Mylyn tasks from failures
It is possible to add plug-ins in Eclipse IDE that will be
notified of JUnit runs and results
Can be used to ensure that tests all run before commits
5. PR0031 - 2010-11-01
5
Where to put Tests
Tests of plug-ins can be put several places each with separate pros and cons
Inline in the application plug-ins
Pros: Easy to manage, access to everything
Cons: In the final product
In separate plug-ins
Pros: Separate from the product
Cons: Only access to the exported interface
In attached fragments
Pros: Access to everything, separate from the product
Cons: A different “thing” from plug-ins, difficult to test via PDEbuild
6. PR0031 - 2010-11-01
6
What can be Tested using JUnit
Almost everything!
Functional APIs
Behavior of UI elements
Performance
It is primary the look of the UI that cannot be tested
To do this, use special UI testing tools
Known SWT aware tools
QF-Test
GUIdancer
WindowTester
But remember SWT is mostly just a thin layer on top of the native widget set, so normally
the native UI testers can be used as well
A few Eclipse elements can be very difficult to test and will require extra co-operation from the
tested code
Dialogs
Wizards
Basically everything that creates a new event loop
7. PR0031 - 2010-11-01
7
Testing with JUnit 4
Create a new plug-in or fragment
Add dependency on “org.junit4”
Create a test class
Alternatively use JUnit Test Case wizard
Annotate test methods with @Test
Annotate common setup and clean-up code with
@Before and @After: Will be executed for each test in class
@BeforeClass and @AfterClass: Will be executed once per class
public class ManagerSizesTest {
private Manager m;
@Before
public void setup() {
m = Manager.getManager();
}
@Test
public void testAttributeProviders() {
assertEquals(true, m.isReady());
}
}
8. PR0031 - 2010-11-01
8
Testing Perspectives, Views and Editors
Perspectives
IWorkbenchWindow.openPage(String perspectiveId, IAdaptable input)
Command org.eclipse.ui.perspectives.showPerspective
Editors
IDE.openEditor(IWorkbenchPage page, IFile file)
One of a large number of methods
IWorkbenchPage.findEditor(IFile file)
No specific command
Views
IWorkbenchPage.showView(String id)
Will open the view in the site according to the perspective definition
Command org.eclipse.ui.views.showView
9. PR0031 - 2010-11-01
9
Testing Interaction
UI tests often have to interact with the application in a manner that is identical or at least similar
to the interaction by a user
Several way to interact
Directly via generated events – especially mouse and key related events
Indirectly via manipulation of SWT widgets
Indirectly via commands
Indirectly via handlers
Which way to use often depends on the level of knowledge of the application implementation
permitted by the tester
In all cases you also need to handle the event loop as tests often runs in the event thread
10. PR0031 - 2010-11-01
10
Test Interaction – Mouse and Key
A subset of generated mouse and key events can be handled via Display.post(Event)
KeyDown, KeyUp
Key down and up must be paired
Modifiers must be “pressed” separately
Textual descriptions of key strokes can be converted to key codes via
KeyStroke.getInstance(stroke)
MouseDown, MouseUp
MouseMove
Mouse must be moved before “pressed”
Double-click is handled by clicking twice
MouseWheel
11. PR0031 - 2010-11-01
11
Test Interaction – Mouse and Key
public static void postMouse(Control c, Point p) {
final Point pt = c.getDisplay().map(c, null, p);
final Event e = new Event();
e.type = SWT.MouseMove;
e.x = pt.x;
e.y = pt.y;
assertTrue(c.getDisplay().post(e));
yield();
e.type = SWT.MouseDown;
e.button = 1;
e.count = 1;
assertTrue(c.getDisplay().post(e));
yield();
e.type = SWT.MouseUp;
e.button = 1;
e.count = 1;
assertTrue(c.getDisplay().post(e));
yield();
}
12. PR0031 - 2010-11-01
12
Test Interaction – The Event Thread
If the test runs in the event thread, event dispatching can be needed.
Required after use of Display.post(Event)
To dispatch all events in the event queue use (often known as yield)
To dispatch all paint requests for a control – possible children
To wait a certain period while dispatching events use
while (display.readAndDispatch()) {
}
cont = false;
display.timerExec(msec, new Runnable() {
@Override
public void run() {
cont = true;
}
});
while (!cont) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
control.update();
13. PR0031 - 2010-11-01
13
Test Interaction – Direct Manipulation of Widgets
A lot of testing can be done by manipulating widgets directly
Cons: it requires an intimate knowledge of the application
Pros: it is very robust to many types of changes in the application
public class TestView extends ViewPart {
public Text myText;
@Override
public void createPartControl(Composite parent) {
Composite top = new Composite(parent, SWT.NONE);
top.setLayout(new GridLayout(1, false));
myText = new Text(top, SWT.SINGLE | SWT.LEAD | SWT.BORDER);
myText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
myText.setText("");
}
@Override
public void setFocus() {
}
}
14. PR0031 - 2010-11-01
14
Test Interaction – Direct Manipulation of Widgets
public class TestViewTest {
private TestView myView;
@Before
public void before() {
try {
IWorkbenchPage page = PlatformUI.getWorkbench().
getActiveWorkbenchWindow().getActivePage();
myView = (TestView) page.showView("com.rcpcompany.appl.views.TestView");
assertNotNull(myView);
} catch (final Exception ex) {
fail(ex.getMessage());
}
}
@Test
public void testText() {
myView.myText.setText("hello");
// Testing the result…
}
}
15. PR0031 - 2010-11-01
15
Test Interaction – Direct Manipulation of Widgets
Some often used cases
Entering characters into a text widget: Text.setText(str)
Selecting item in combo box: Combo.setText(str) or Combo.select(i)
Ticking check boxes and radio buttons: Button.setSelection(b)
But not push buttons!
Moving scrollbars, sliders, and scales: ScrollBar.setSelection(i)
Changing a tab in a tab folder: TabFolder.setSelection(i)
Though no selection event is posted
16. PR0031 - 2010-11-01
16
Test Interaction – Executing Simple Commands
A simple command has no parameters
IHandlerService hs = …;
hs.executeCommand("<command-id>", null);
17. PR0031 - 2010-11-01
17
Test Interaction – Executing Parameterized Commands
A parameterized command has one or more parameters and can be created in one of two ways:
IHandlerService hs = …;
ICommandService commandService = …;
Command c = commandService.getCommand("<command-id>");
IParameter param = c.getParameter("<parameter-id>");
Parameterization[] parms = new Parameterization[] { new Parameterization(param,
"<value>") };
ParameterizedCommand pc = new ParameterizedCommand(c, parms);
hs.executeCommand(pc, null);
IHandlerService hs = …;
ICommandService commandService = …;
ParameterizedCommand pc = commandService.deserialize(”<cmd-id>(<p-id>=<value>)");
hs.executeCommand(pc, null);
ParameterizedCommand pc =
commandService.deserialize(”org.eclipse.ui.views.showView(“ +
“org.eclipse.ui.views.showView.viewId=org.eclipse.ui.views.ContentOutline)");
18. PR0031 - 2010-11-01
18
Testing Tables
Tables can be a little more difficult to manage
Finding the bounds of a specific cell: Table.getItem(row).getBounds(column)
Finding the bounds of a specific header:
Adjust y to 0 and height to Table.getHeaderHeight()
If sorting a table, sleep for at least 5-600ms
The row element for a specific row in a viewer: Table.getItem(row).getData()
19. PR0031 - 2010-11-01
19
Parameterized Tests
It can be very beneficial to parameterize UI tests
Test data can even be generated
@RunWith(Parameterized.class)
public class UIAttributeFactoryTest<T extends Widget> extends BaseUIAttributeFactoryTest<T> {
@Parameters
public static Collection<Object[]> data() {
return Arrays.asList(new Object[][] {
{ Button.class, SWT.PUSH, "", String.class, "text" },
{ Button.class, SWT.PUSH, "text", String.class, "text" },
{ Button.class, SWT.PUSH, "background", Color.class, "background" },
…
});
}
public UIAttributeFactoryTest(Class<T> widgetType, int style, final String attribute,
final Class<?> expectedValueType, String property) {
...
}
@Test
public void test() {
…
}
}
20. PR0031 - 2010-11-01
20
Testing no Error Messages are Logged
Most parts of the Eclipse RCP framework log errors using the log interface of plug-ins
To test that no error messages are logged hook into the Platform log listener interface
public static void assertNoLog(Runnable run) {
final NoLogListener ll = new NoLogListener();
Platform.addLogListener(ll);
try {
run.run();
} catch (final Exception ex) {
fail("Exception occured: " + ex.getClass() + ": " + ex.getMessage());
}
Platform.removeLogListener(ll);
assertTrue("Log message: " + ll.lastMessage, ll.called == 0);
}
private static class NoLogListener implements ILogListener {
public int called = 0;
public String lastMessage;
public void logging(IStatus status, String plugin) {
if (status.getSeverity() != IStatus.ERROR) return;
called++;
lastMessage = status.getMessage();
}
}
21. PR0031 - 2010-11-01
21
If You Want to Know More about Testing
“JUnit 4 in 60 Seconds”
http://www.cavdar.net/2008/07/21/junit-4-in-60-seconds/
Short but very concise and with focus on the important stuff
“JUnit.org”
http://www.junit.org/
Home of all the xUnit technologies
“List of GUI testing tools”
http://en.wikipedia.org/wiki/List_of_GUI_testing_tools