Kiril Mitov’s blog about Eclipse

An Eclipse Committer and Project Blogs weblog

Unit testing a wizard

I will again use JPicus and its only wizard (for now) to share my experience with testing wizards.

Wizads have always been a subject of low code coverage and no unit testing. Many examples could be given were the whole test suite of a project is green, but when a user starts using the product there are an enormous number of errors. And generally the user starts with the wizards…

As everything in JPicus the NewConnectionWizard is 100% covered with test.

This are coverage results from unit testing the wizard package:

This are coverage results from integration testing* the wizard package:

Combined the result is 100% ;)

*With “integration test” I mean a JUnit test that includes creating of more then 3 (inclusive) productive code objects. A test that verifies the correct integration between objects, but only the tiny little places of integration. Integration tests are something written and executed by the developer.

First - the provided functionality:

The NewConnectionWizard allows a user to create a new connection to a JPicus agent. There is only one page where a host and port are entered. If the port is not valid the page should show an error message. If there is no such host an error dialog should be shown when pressing Finish. When the user presses Finish a new connection should be created and shown in the AgentConnectionsView.

Second - from an eclipse developer point of view we have:

Instances of IWizard and IWizardPage. The wizard page contains a Composite with two Labels and two Text fields. A ModifyListener is responsible for handling modification of the host and port text fields.

Third - the Tests:

The first thing about wizard pages is that there should be no business logic in the wizard page. Create a “controller” and put all this logic in it. In this case the HostAndPortPageController. Create an interface for this controller and inject it in the wizard page. I recommend passing the controller as an argument to the constructor of the page. And there is no default constructor. The page can not leave without its controller. Do not leave the page to create the controller by itself. Invert the dependency (DIP).

public HostAndPortWizardPage(IHostAndPortPageController controller) {
super(CONNECTION_PROPERTIES);
setDescription(Messages.HostAndPortWizardPage_specifyHostAndPort);
setTitle(CONNECTION_PROPERTIES);
setImageDescriptor(JPicusUI.getImageDescriptorForPath(”icons/pecker64.png”));
this.controller = controller;
}


Creating a controller for every page might seem tedious at first. But it will pay off. And if you are have a dependency to Web Tools Platform then using the Data Model Wizards framework is more then appropriate.

Since there is no business logic in the wizard what should be tested?

Is there a SWT control of the page and is this control actually a child of the passed shell? (Here createControl() creates a new HostAndPortWizardPage, calls page.createControl(shell) and returns the page).

@Test
public void testGetControl() {
IWizardPage page = createControl();
assertEquals(1, shell.getChildren().length);
assertSame(shell.getChildren()[0], page.getControl());
}

Are the correct children of the control created?

@Test
public void testControlChildren() {
Composite composite = getControl();
assertEquals(4, composite.getChildren().length);
assertEquals(Label.class, getHostLabel(composite).getClass());
assertEquals(Text.class, getHostText(composite).getClass());
assertEquals(Label.class, composite.getChildren()[2].getClass());
assertEquals(Text.class, getPortText(composite).getClass());
}

Is the layout of the wizard correct? (Do not test whether the layout is working. It is a SWT layout so it is working. Test only that the correct layout with the correct properties is set to the composite).

@Test
public void testControlLayout() {
Composite composite = getControl();
GridLayout layout = (GridLayout) composite.getLayout();
assertEquals(2, layout.numColumns);
assertFalse(layout.makeColumnsEqualWidth);
}

Are the correct checks made when modifying a text field?

@Test
public void testModifyPortNotANumber() {
expect(controller.checkPort(”not a number”)).andReturn(”error message”);
// test
IWizardPage page = createControl();
Text port = getPortText((Composite) shell.getChildren()[0]);
port.setText(”not a number”);
assertEquals(21500, ((HostAndPortWizardPage) page).getPort());
assertEquals(”error message”, page.getErrorMessage());
}

The left of the wizard page implementation is mostly interacting with the controller. Since the controller is represented by an interface it allows for a good abstraction of the details. Having a good mock library (in my case easymock) testing the wizard page and reaching 100% coverage, with meaningful tests*, is straightforward.

*With meaningful tests I refer to the rule to write tests that will break when a productive code is change. Writing tests for the purpose of only reaching a good code coverage is useless and dangerous.

I have heard comments that this wizard page is very simple and that`s way it could be unit tested, but if a wizard page is too complicated to be unit tested isn`t it too complicated for the user to use?

As a result - wizard pages can be tested. In Eclipse it has always been this way…

How are your wizard pages tested?

Posted June 16th, 2009 by kiril in category: JPicus
You can leave a response, or trackback from your own site.

Leave a Reply

You must be logged in using your Eclipse Bugzilla account to post a comment.

Recent Posts

Archives

Categories

Meta