A little bit on unit testing code with major runtime dependency
There are times when you want to unit test a part of your code. Maybe for proof-of-concept that it works, or you just wanna be sure that it works before you hit that execute button.
The thing is, that part of the code requires that a runtime mechanism be running.
Am I talking gibberish ? Let me give you an example: Suppose you are writing an eclipse plugin, and you are writing an action that would delete an item from a tree. That’s a fairly generic operation, so you start writing an action class that accepts an object and another object that you can use to find the parent of that object. Then you write the actual action method which finds the parent of this object and deletes it.
public class DeleteItemAction extends something
{
private ITreeContentProvider provider;
public DeleteItemAction( ITreeContentProvider provider )
{
this.provider = provider;
}
public void doAction( Object object )
{
Object parentObject = contentProvider.getParent( object );
( (Something) parentObject ).removeChild( object );
}
}
Perfect. Now, I can write a test for this code.
@Test
public void testDeleteItemAction()
{
List
assertTrue(objectList.contains( firstObject ) );
DeleteItemAction action = new DeleteItemAction( provider );
action.doAction( firstObject );
assertFalse( objectList.contains( firstObject ) );
}
Then you suddenly remember that you have to prompt your user if he really wants to delete it or cancel. So you then scrambled to your other codes and look for that message-box-launching code,.. you found it,.. you copy-paste it.
public class DeleteItemAction extends something
{
private ITreeContentProvider provider;
public DeleteItemAction( ITreeContentProvider provider )
{
this.provider = provider;
}
public void doAction( Object object )
{
MessageBox messageBox =
new MessageBox( PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(), SWT.ICON_QUESTION |
SWT.YES | SWT.NO );
messageBox.setMessage( "Do you want to delete this object" );
messageBox.setText( "Delete object?" );
int response = messageBox.open();
if ( response == SWT.YES )
{
Object parentObject = contentProvider.getParent( object );
( (Something) parentObject ).removeChild( object );
}
}
}
And you smirk and say, damn, I’m good. Then you run your test.
Surprise. It doesn’t run. The code you copy and pasted pops up a message box,.. and you need to run your test as an eclipse plugin test in order for the entire eclipse runtime mechanism to be running when you perform the test,.. for the message box to pop-up! The hassles you get for a want of a test to exercise a simple code.
So what do you do ? Do you give up and just click “Run eclipse plugin test” and wait for ages for it to load? Or do you restructure your code so you can still just “Run junit test” and still be able to test your code ?
I prefer the latter. Turns out, it was easy.
Abstract. Abstract the part that requires the runtime mechanism. In this case, the code that calls the message box. Move it out of the generic code, make it protected, so it could be overridden. And when you write your junit test, create a mock class that extends it and override the “heavy code” with something that just returns true.
To illustrate:
public class DeleteItemAction extends something
{
private ITreeContentProvider provider;
public DeleteItemAction( ITreeContentProvider provider )
{
this.provider = provider;
}
public void doAction( Object object )
{
if ( showWarningAndPrompt() )
{
Object parentObject = contentProvider.getParent( object );
( (Something) parentObject ).removeChild( object );
}
}
protected boolean showWarningAndPrompt()
{
MessageBox messageBox =
new MessageBox( PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(), SWT.ICON_QUESTION |
SWT.YES | SWT.NO );
messageBox.setMessage( "Do you want to delete this object" );
messageBox.setText( "Delete object?" );
int response = messageBox.open();
if ( response == SWT.YES )
return true;
else
return false;
}
}
and in your test
@Test
public void testDeleteItemAction()
{
List
assertTrue(objectList.contains( firstObject ) );
DeleteItemAction action = new DeleteItemActionMock( provider );
action.doAction( firstObject );
assertFalse( objectList.contains( firstObject ) );
}
private class DeleteItemActionMock extends DeleteItemAction
{
public DeleteItemActionMock( ITreeContentProvider contentProvider )
{
super( contentProvider );
}
protected boolean showWarningAndPrompt()
{
return true;
}
}
The latter doesn’t need the entire eclipse runtime to run,.. it’s just another junit test.
Additional benefit ? Your code has now a better structure.
I believe this technique has a name already. I just don’t know.
I hate it when wordpress mangles my code.
- BROWSE / IN TIMELINE
- « It’s hard being a geek
- BROWSE / IN Uncategorized
- « It’s hard being a geek
SPEAK / ADD YOUR COMMENT
Comments are moderated.


Recent Comments