eclipse summit europe '10 - test ui aspects of plug-ins
DESCRIPTION
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.TRANSCRIPT
PR0031 - 2010-11-01
Redistribution and other use of this material requires written permission from The RCP Company.
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.
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
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
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
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
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
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;
@Beforepublic void setup() {
m = Manager.getManager();}
@Testpublic void testAttributeProviders() {
assertEquals(true, m.isReady());}
}
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
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
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
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();
}
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() {
@Overridepublic void run() {
cont = true;}
});while (!cont) {
if (!display.readAndDispatch()) {display.sleep();
}}
control.update();
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;
@Overridepublic 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("");
}
@Overridepublic void setFocus() {}
}
PR0031 - 2010-11-01
14
Test Interaction – Direct Manipulation of Widgets
public class TestViewTest {private TestView myView;
@Beforepublic 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());
}}
@Testpublic void testText() {
myView.myText.setText("hello");// Testing the result…
}}
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
PR0031 - 2010-11-01
16
Test Interaction – Executing Simple Commands
A simple command has no parameters
IHandlerService hs = …;
hs.executeCommand("<command-id>", null);
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)");
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()
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> {
@Parameterspublic 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) {
...}
@Testpublic void test() {
…}
}
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();
}}
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