About Me

My photo
I'm project manager of a software development team at www.researchspace.com. I've been developing bioinformatics software for the last 9 years or so, and I'm hoping to share some of the experience and knowledge I've gained with Eclipse RCP, Java web app development and software development in general on these blogs.

Thursday 2 October 2008

SWTBot - gui testing for RCP?

I've been looking for a GUI testing tool for Eclipse RCP for a while with little success - open source solutions tend to have astepp learning curve while commercial tools such as Squish or WindowTester I've never got to work properly.

Today I've been looking at SWTBot which looks really promising. It provides complete access to the underlying SWT widgets, has certain Eclipse specific methods for accessing views and editors,
and can be run just like a regular JUnit test.

I'm just getting started with a new project that is currently just a single plugin.
To create tests using SWTBot, I created a new regular plugin project that depends on my RCP project and the SWTBot plugins. By putting the tests their own plugin it avoids my own project having dependencies on SWTBot and other transitive dependencies such as JDT.

Running the tests couldn't be easier - write a test case extending SWTBotEclipseTestCase
and add your tests.

My first test tests my own new project creation wizard - here is the code for it:


public class testSWT1 extends SWTBotEclipseTestCase {
// stuff you can do with Eclipse
@SuppressWarnings("unchecked")
public void testDoSomethingInterestingWithEclipse() throws Exception {

bot.menu("File").menu("New Wizards").menu("Other...").click();
bot.shell("New").activate();
selectSBSINewProjectWizard();
assertEnabled(bot.button("Next >"));
bot.button("Next >").click();


bot.shell("New SBSI Project").activate();


assertFalse(bot.checkBox("Butt1").isChecked()); // default
assertNotEnabled(bot.button("Finish"));
bot.label("Project name: ").getText();

SWTBotText text = bot.text("");
assertTrue(canFinishWithName("NAME",text));
assertTrue(canFinishWithName(" NAME",text));// trailing space OK
assertFalse(canFinishWithName("..NAME",text));
assertFalse(canFinishWithName("N AME",text));




}

private boolean canFinishWithName(String name, SWTBotText text) throws WidgetNotFoundException,
InterruptedException {
text.setText(name);
Thread.sleep(500);
return bot.button("Finish").isEnabled();
}

private void selectSBSINewProjectWizard() throws WidgetNotFoundException {
SWTBotTree tree = bot.tree();
tree.expandNode("SBSI");
SWTBotTreeItem item= tree.getTreeItem("SBSI");

SWTBotTreeItem item2 = item.getNode("New SBSI Project wizard");
item2.select();
}

This test navigates the standard Eclipse New-> dialogs and then tests a creation wizard's ability to finish based on the name typed in.

What I particularly like are:
  1. It's expressive
  2. It will be straightforward to write helper classes for more complex widgets like wizards
  3. It runs in real time so you can see what's going on.
One thing to note is that widgets are identified by their text or title rather than any concept of ID. Therefore you need to be careful to get the name right, especially with spaces.

Also, I came a cropper with bot.button("name") for a checkbox. Whilst a checkbox is a Button type inSWT, SWTBot has its own method bot.checkbox("name") which must be used instead.

Some mysteries remain - how to access a context menu for a view when you don't know its name?

Thanks for reading

Monday 29 September 2008

Testing selection actions in GEF (2)

In my previous blog (Testing selection actions in GEF (1), I discussed ways to test selection actions in GEF, and we got as far as testing the calculateEnabled() method.

Getting started
If you're following these blogs, this one carries straight on from the previous one. If you're diving straight in here, you can download the modified 'Shapes EXample' plugin here, and you just need to import it as a plugin project with the source code.

Now we'll move and test the run() method, again using the SrcSelectAction example.

To recap, this is the code for the action which just selects the src shape of a connection:

public void run() {
ConnectionEditPart cep = (ConnectionEditPart) getSelectedObjects().get(0);
EditPartViewer viewer = cep.getViewer();

viewer.deselectAll();
viewer.flush();
viewer.select(cep.getSource());

viewer.reveal(cep.getSource());
}
In order to test this method , we need to to a bit of work to set up viewers and edit pparts for testing. Methods called on the viewer tend to end up calling graphical code and so we need to stub out this behaviour. Again, once we have set this up we will have a good framework for testing other action classes we might write.

Creating stub classes

First of all we need to stub out the refreshVisuals() methods of edit parts.

First, create the package org.eclipse.gef.examples.shapes.parts in the 'test' folder. Then,
create the following classes in this package:


class StubConnectionEditPart extends ConnectionEditPart {
String propName;
public void propertyChange(PropertyChangeEvent event) {
this.propName = event.getPropertyName();
}

String getPropertyCalled (){
return propName;
}

}


and


class ShapeStubEditPart extends ShapeEditPart {

protected void refreshVisuals() {

}

String propName;
public void propertyChange(PropertyChangeEvent event) {
this.propName = event.getPropertyName();
}

String getPropertyCalled (){
return propName;
}
}
These are test-Spy classes - they store the details of the property change event that occurs,
and provide an accessor to those details, at the same time as stubbing out the 'visual' code.

We also need to override the EditPartFactory to create these stubs:
Copy and paste the Shapes edit part factory into the test/org.eclipse.gef.examples.shapes.parts folder, and edit the code to return
the stub edit parts.


private EditPart getPartForElement(Object modelElement) {
if (modelElement instanceof ShapesDiagram) {
return new DiagramEditPart();
}
if (modelElement instanceof Shape) {
return new ShapeStubEditPart(); //returns a stub
}
if (modelElement instanceof Connection) {
return new StubConnectionEditPart(); //returns stub
}
throw new RuntimeException(
"Can't create part for model element: "
+ ((modelElement != null) ? modelElement.getClass().getName() : "null"));
}
Finally we need to provide a stub viewer implementation which replaces the actual visual code
with test-spy methods that recored the fact the methods were called:


ViewerStub () {
setEditPartFactory(new ShapesStubEditPartFactory());
}

protected LightweightSystem createLightweightSystem() {
return null;
}

public void flush(){
timesFlushCalled++;
}

public void appendSelection(EditPart ep){
super.appendSelection(ep);
timesAppendCalled++;
}

public void reveal (EditPart ep) {
this.revealed = ep;
}
/**
* Test-spy method to check which EP was revealed.
* @return the revealed edit part
*/
public EditPart getRevealed() {
return revealed;
}



Providing a test base class
Now we need to create a framework that connects all these stubs. This class can now become a base class for all our action test classes:

package org.eclipse.gef.examples.shapes.parts;

import java.util.Map;

import org.eclipse.gef.GraphicalViewer;
import org.eclipse.gef.examples.shapes.model.Connection;
import org.eclipse.gef.examples.shapes.model.EllipticalShape;
import org.eclipse.gef.examples.shapes.model.RectangularShape;
import org.eclipse.gef.examples.shapes.model.Shape;
import org.eclipse.gef.examples.shapes.model.ShapesDiagram;

/**
* Sets up a test framework for testing Selection Actions
*
* @author Richard Adams
*
*/
public class EditPartTestModelSetUp {

protected Shape LINK_TARGET, LINK_SOURCE;
protected ShapesDiagram DIAGRAM;
protected Connection CONN;

protected DiagramEditPart diagramPart;

protected ViewerStub viewerTSS = new ViewerStub();
protected GraphicalViewer viewer = viewerTSS;

protected final int NUM_ALL_SHAPES = 2;

final int NUM_LINKS = 1;

protected void generateModel() {
LINK_TARGET = new EllipticalShape();
LINK_SOURCE = new RectangularShape();
CONN = new Connection(LINK_SOURCE, LINK_TARGET);
DIAGRAM = new ShapesDiagram();
DIAGRAM.addChild(LINK_SOURCE);
DIAGRAM.addChild(LINK_TARGET);

}

protected void createEditPartList() {
generateModel();

ShapesEditPartFactory fac = new ShapesEditPartFactory();

// initialise edit part creation
diagramPart = (DiagramEditPart) fac.createEditPart(null, DIAGRAM);

// activate parent
viewer.getRootEditPart().activate();
// this creates the edit part tree
viewer.setContents(diagramPart);

}
}


This class creates a basic model, and subclasses calling createEditPartList()
will initialise the model,the edit parts and the viewer.

Saturday 27 September 2008

Testing Selection Actions in GEF (1)

Unit testing is pretty much an essential part of most Java development these days, and tools such as JUnit and their IDE integration make it much easier to get started.

It's always easier to test code that you've written yourself or have control over, but slightly less so when your code interacts with a large framework such as GEF, JFace or Draw2d that involve graphical operations. If the class you want to test has a number of superclasses, often some superclass will call a method that assumes the Eclipse platform is running or that there is a graphical display initialized.

This is the case with testing GEF classes. My solution to these issues is to use a combination of mock objects and stubs to insulate the class under test. In this blog I'm going to carry on from where I left off in my previous blog Selection Actions in GEF and describe my approach to writing unit tests involving edit parts and selection actions. Although it may seem an effort to set up, it is a 'one-time operation' that enables rapid testing of subsequent classes.

We'll start off by writing tests for the calculateEnabled() method of SrcSelectAction.
To begin with, download the Shapes Example plugin and import it into your workspace as a plugin project.
In order to mock objects, we'll use the JMock library. Download the library, add the jars into the shapes plugin project and add it to the build path. I'm using version 2.2.0 but download the latest version. We will only use mock objects in a trivial way in this example but there are excellent tutorials on the JMock website if you want to discover more.
Next, create a new source folder called 'test' and in it create the package

org.eclipse.gef.examples.actions

Now we must remember to add JUnit 4 to your project libraries , and finally we'll
add a new JUnit test case 'SrcSelectAction' into the new package.

You should end up with a project structure like this:



Setting up the test class

First of all we will create a mock IWorkbenchPart that is needed for the constructor of a selection action. In JMock, we need to add the following lines:


/*
* Tells test executor to use JMock to run this test case.
*/
@RunWith (JMock.class)
public class SrcSelectActionTest {
SrcSelectAction actionAPI; // reference to action class we're testing

Mockery mockery = new JUnit4Mockery(); // initialise the mock object system
/*
* Create a mocked IWorkbenchPart
*/
final IWorkbenchPart part = mockery.mock(IWorkbenchPart.class);


Now, we need to find a way to get a list of edit parts from the getSelectedObjects() method. In application code, this calls the Eclipse selection service which returns the current selection. Since this is a unit test none of this is available, so we'll create a Test Specific subclass of our action which will override getSelectedObjects(). We could hard code a list to be returned, but as we may want to run tests with different selections ,we will add a private method setSelectedEditParts

private class SrcSelectActionTss extends SrcSelectAction {
private List selectedObjects;
public SrcSelectActionTss(IWorkbenchPart part) {
super(part);
}
private void setSelectedObjects (List selectedObjects) {
this.selectedObjects=selectedObjects;
}
protected List getSelectedObjects (){
return selectedObjects;
}

}

Now, in the test set up we'll create the action. It is good practice when using test-specific subclasses to have two references to the action class- one defined as a
a test-specific subclass and one defined as the class we're testing (actionAPI). This makes it easier to ensure that we only test methods in the real action class and don not start testing the behaviour of the test specific subclass!

SrcSelectActionTss actionTss;
SrcSelectAction actionAPI;
@Before
public void setUp() throws Exception {
actionTss = new SrcSelectActionTss(part);
actionAPI =actionTss;
}


Writing the tests

Finally we're ready to crank out some tests!!


@Test
public void actionEnabledForSingleSelectedConection () {
//set up model and edit part
ConnectionEditPart connEP = new ConnectionEditPart();
connEP.setModel(new Connection(new RectangularShape(), new RectangularShape()));
// here we set in the selected objects as a single connection
actionTss.setSelectedObjects(Arrays.asList( new GraphicalEditPart[]{connEP}));
// assert is Enabled
assertTrue(actionAPI.calculateEnabled());
}


With a 'real' model we would probably have them defined as interfaces and could probably remove the dependency of the test on creating model objects for the edit part.
Now that we're set up though we can churn out some tests pretty fast with little effort. E.g.,


@Test
public void actionNotEnabledIfNothingSelected () {
actionTss.setSelectedObjects(Collections.EMPTY_LIST);
assertFalse(actionAPI.calculateEnabled());
}


Conclusion

Here is the final full listing of the SrcSelectActionTest :

package org.eclipse.gef.examples.actions;

import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;

import org.eclipse.gef.GraphicalEditPart;
import org.eclipse.gef.examples.shapes.model.Connection;
import org.eclipse.gef.examples.shapes.model.RectangularShape;
import org.eclipse.gef.examples.shapes.parts.ConnectionEditPart;
import org.eclipse.ui.IWorkbenchPart;
import org.jmock.Mockery;
import org.jmock.integration.junit4.JMock;
import org.jmock.integration.junit4.JUnit4Mockery;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;

/*
* Tells test executor to use JMock to run this test case.
*/
@RunWith (JMock.class)
public class SrcSelectActionTest {
SrcSelectAction actionAPI; // reference to action class we're testing

Mockery mockery = new JUnit4Mockery(); // initialise the mock object system
/*
* Create a mocked IWorkbenchPart
*/
final IWorkbenchPart part = mockery.mock(IWorkbenchPart.class);

SrcSelectActionTss actionTss; // reference to TSS
private class SrcSelectActionTss extends SrcSelectAction {
private List selectedObjects;
public SrcSelectActionTss(IWorkbenchPart part) {
super(part);
// TODO Auto-generated constructor stub
}
private void setSelectedObjects (List selectedObjects) {
this.selectedObjects=selectedObjects;
}
protected List getSelectedObjects (){
return selectedObjects;
}

}

@Before
public void setUp() throws Exception {
actionTss = new SrcSelectActionTss(part);
actionAPI =actionTss;
}

@Test
public void actionEnabledForSingleSelectedConection () {
//set up model and edit part
ConnectionEditPart connEP = new ConnectionEditPart();
connEP.setModel(new Connection(new RectangularShape(), new RectangularShape()));

actionTss.setSelectedObjects(Arrays.asList( new GraphicalEditPart[]{connEP}));
assertTrue(actionAPI.calculateEnabled());
}

@Test
public void actionNotEnabledIfNothingSelected () {
actionTss.setSelectedObjects(Collections.EMPTY_LIST);
assertFalse(actionAPI.calculateEnabled());
}
@After
public void tearDown() throws Exception {
}

}



As I've hopefully demonstrated, an initial set up should reap dividends for writing unit tests for multiple actions. By using mocks and test specific subclasses, we're able to write specific, clear tests for the behaviour of the class without a large set up of the GEF framework. The test specific subclass defined here could be reused multiple times for other selection actions, if we made it a regular class rather than an inner class.

We've found JMock invaluable in our project, for mocking Eclipse components that might be unavailable in a JUnit environment.

In my next blog I'll cover how to test some action run() methods. These can require a little more set up but again the work we put in can be reused in multiple test cases.

The book X-Unit Test Patterns is a great source of ideas for improving one's test code.

Thanks for reading!

Wednesday 24 September 2008

Selection actions in GEF

In this blog, I will demonstrate how to use Selection Actions in GEF. These are actions which can be enabled via the context menu, dependent on the items selected in the editor. They are specifically useful for performing operations on selected edit parts, via their getSelectedObjects() method.

System setup
I'm using Windows XP and Eclipse Ganymede (3.4), but Eclipse 3.3 or 3.2 should work as well and the examples should be platform independent. To follow this tutorial, download the GEF-SDK complete, and unzip the example plugins into the 'Drops' directory of your Eclipse IDE ( or just the plugins directory if you're running Eclipse 3.3 or earlier). Restart Eclipse, and create a Shapes Plugin Project in Eclipse by
File->New->Example->GefPlugins->ShapesExample.

At the end of this stage you should have the shapes example plugin project in your workspace, with source code.

Assumed knowledge of reader
This tutorial is designed for a developer who has read the 'Shape Diagram Editor' tutorial available from the GEF website, and has some knowledge of the basic GEF framework but is looking to extend their knowledge.

Selection Action Example

Our example task will be to create an action that when run, will select, and scroll to, the source or target of a connection. This action is useful in complex diagrams with long connections where the connection endpoints are outside the viewable area of the diagram. This action will just be enabled when a single connection is selected.

Step 1: Creating the action

First of all, lets create a new package for editor actions, org.eclipse.gef.examples.actions, in the Shapes Example plugin, and in it create a new class, SrcSelectAction that extends org.eclipse.gef.ui.actions.SelectionAction. After creation, the class will look like this

package org.eclipse.gef.examples.actions;

import org.eclipse.gef.ui.actions.SelectionAction;
import org.eclipse.ui.IWorkbenchPart;

public class SrcSelectAction extends SelectionAction {
public SrcSelectAction(IWorkbenchPart part) {
super(part);
}

protected boolean calculateEnabled() {
// TODO Auto-generated method stub
return false;
}

}


For now, add the following lines:

public class SrcSelectAction extends SelectionAction {
// an identifier for the action
public static final String ID="shapes.tutorial.SrcSelectAction";
public SrcSelectAction(IWorkbenchPart part) {
super(part);
setId(ID); // sets ID
setText("Select link src"); // sets text displayed in the menu
setToolTipText("Focus on link source");
}
....
}




Later, we'll add an implementation for the calculateEnabled method and override
public void run().
Now however we need to make sure the action is registered with the editor and is created properly.

Step 2 - Registering the action with the editor.
The GraphicalEditor class provided by GEF creates some standard actions for
Copy, Delete etc using the method

protected void createActions()

In the ShapesEditor class, we can override this method to create our own actions:

protected void createActions() {
super.createActions(); // important else we won't get the default actions!
IAction action = new SrcSelectAction(this);
getActionRegistry().registerAction(action);
getSelectionActions().add(action.getId());
}


Now we will add the action to the editor context menu:

In the class ShapesEditorContextMenuProvider add the following lines to buildContextMenu(IMenuManager menu)

IAction action = getAction(SrcSelectAction.ID);
if (action.isEnabled()){
menu.appendToGroup(GEFActionConstants.GROUP_COPY, action);
}


This code will add the action to the context menu if the result of the action's calculateEnabled() method returns true.

Step 3 - Enabling the action

Now let's go back implement the calculateEnabled() method in SrcSelectAction:

protected boolean calculateEnabled() {
// we only want enabled if is single selection
if(getSelectedObjects().size() !=1
|| (!(getSelectedObjects().get(0) instanceof ConnectionEditPart))){
return false;
}
// and we want the model to be a Connection object.
ConnectionEditPart cep = (ConnectionEditPart)getSelectedObjects().get(0);
if(cep.getModel() instanceof Connection ){
return true;
}
return false;

}



Step 4 - Running the action

In the run() method we need to get the connection source and invoke methods on the edit part's viewer to select and reveal it.

public void run() {
/*this method is only called if calculate enabled() returns true
* so we know its a ConnectionEditPart
*/
ConnectionEditPart cep = (ConnectionEditPart)getSelectedObjects().get(0);
/*Any EditPart can access its viewer
*/
EditPartViewer viewer = cep.getViewer();

/* Deselect an existing selection */
viewer.deselectAll();

/* Flush this */
viewer.flush();

/* Select the source edit part */
viewer.select(cep.getSource());

/* If the source Edit part is off screen, this will scroll to it.
viewer.reveal(cep.getSource());
}




Step 5 Deploying the plugin

  1. Export the plugin using File->Export->Plugins->DeployablePlugins and Fragments
  2. Quit Eclipse
  3. Replace the existing Shapes Example plugin with your deployed version
  4. Restart Eclipse
  5. Try to create a new example shapes diagram.


Now, try creating 2 shapes and a link between them. If you select the connection and get the context menu you should see the 'Select Link Src' action in the menu. If you select this action then the link source will be selected.

Step 6 Summary

In this blog I've described how to create a simple SelectionAction in GEF. This example can easily be extended to any other action involving a selection, for example selecting all edit parts with a particular model type.

Thanks for reading this, in the next blog I'll talk about my approach to testing actions in GEF.

Welcome to my blog

Welcome to this new blog about GEF !

In this series of blogs I hope to publish some ideas on how to use the Eclipse Graphical Editing Framework, GEF, based on my own experience over the last 2 years developing a graphical editor for systems biology (EPE, www.bioinformatics.ed.ac.uk/epe). We started off using the 'Logic' and 'Shapes' examples quite extensively as we learned the system but have found a lot more useful functionality in GEF than is made readily available.

In particular I hope to cover in a series of blogs :
  1. Selection actions
  2. Connection decorators
  3. Customizing connections
  4. Grouping of shapes
  5. Image export from GEF
  6. Z-ordering