More Units with MoreUnit

Home  >>  Eclipse  >>  More Units with MoreUnit

More Units with MoreUnit

Just over a year ago I wrote a post about working with JUnit in Eclipse. One of the commenters recommended MoreUnit in order to increase testing efficiency even more. Giving it a try I was delighted and the plugin’s keyboard shortcuts were immediately memorized by my autonomic nervous system…

Additionally, after using MoreUnit for a while I recognized a slight but notable change in one of my working routines. More precisely it seems that I tend to extract more classes covered by own unit tests during refactorings1 than I did before. As maintaining cohesion results in many small classes2 this is probably a good thing.

Well, one could say ‘MoreUnit, the name says it all’ but as I did not expect it to be meant in that way, I decided this might be a good subject for a post. So let’s have a look at the following example to get started:

class ActionItem extends MouseAdapter  {

  private Label control;
  private Runnable action;
  private boolean mouseDown;

  [...]

  ActionItem( Composite parent, Runnable action ) {
    this.control = new Label( parent, SWT.NONE );
    this.control.addMouseListener( this );
    this.action = action;
  }

  @Override
  public void mouseDown( MouseEvent event ) {
    markMouseDown();
  }

  @Override
  public void mouseUp( MouseEvent event ) {
    handleMouseUp( event );
  }

  private void markMouseDown() {
    mouseDown = true;
  }

  private void handleMouseUp( MouseEvent event ) {
    if( mouseDown && inRange( event ) ) {
      action.run();
    }
    mouseDown = false;
  }

  private static boolean inRange( MouseEvent event ) {
    Point size = ( ( Control )event.widget).getSize();
    return    event.x >= 0 && event.x <= size.x 
           && event.y >= 0 && event.y <= size.y;
  }

  [...]
}

The class is an excerpt of a custom UI control3 and it shows how such a control could implement mouse click behavior. We proceed on the assumption that we have an appropriate unit test case with sufficient coverage up and running. It is also safe to presume that several structural refactoring steps have already taken place before reaching the arrangement of the code snippet above.

Considering the mouse click as a single responsibility it is arguable that it should be separated into its own class. To do so I generally start with an inner class where I move the related methods and fields to. After that, I assign an instance of that new type to a new field of the surrounding class as demonstrated in the ActionItem constructor below. Last but not least I fix the now undefined method calls by delegating those calls to the newly created field4:

class ActionItem extends MouseAdapter  {

  private Label control;
  private ClickHandler clickHandler;

  [...]

  ActionItem( Composite parent, Runnable action ) {
    this.control = new Label( parent, SWT.NONE );
    this.clickHandler = new ClickHandler( action );
    this.control.addMouseListener( this );
  }

  @Override
  public void mouseDown( MouseEvent event ) {
    clickHandler.markMouseDown();
  }

  @Override
  public void mouseUp( MouseEvent event ) {
    clickHandler.handleMouseUp( event );
  }

  static class ClickHandler {

    private Runnable action;
    private boolean mouseDown;

    ClickHandler( Runnable action ) {
      this.action = action;
    }

    private void markMouseDown() { [...] }
    private void handleMouseUp( MouseEvent event ) { [...] }
    private static boolean inRange( MouseEvent event ) { [...] }
  }

  [...]

}

By running the ActionItem's test case successfully and checking the coverage once again, one can be pretty sure that the extraction described above did not introduce any mistakes. Because of this it feels fairly save to move the ClickHandler finally to its own file.

However, now there is a class serving as some kind of 'subcomponent' which is only covered indirectly by the 'main' component's test case. I have made the observation that such classes often develop a living of their own. Meaning their functionality expands or they are getting reused by other classes or both. At some point in time, it probably gets rather difficult to add meaningful and/or comprehensible tests. This is because the test class itself lacks the cohesion which was introduced to the unit under test by splitting it into two.

 Testing with JUnit

Testing with JUnit Book

Testing with JUnit is one of the most valuable skills a Java developer can learn. No matter what your specific background, whether you’re simply interested in building up a safety net to reduce regressions of your desktop application or in improving your server-side reliability based on robust and reusable components, unit testing is the way to go.

Frank has written a book that gives a profound entry point in the essentials of testing with JUnit and prepares you for test-related daily work challenges.

  Get It Now! 

For that reason, I usually create a particular test case for the extracted class right away. Even more as with MoreUnit available this can be achieved quite intuitively, actually providing the new test case automatically with a basic set of test method stubs. Continuing our example we would open the extracted ClickHandler in an editor, press 'ctrl+j' 5 and select in the upcoming wizard for which method we would like to have a stub created:

new-test

Pressing finish creates a test case correctly located under the according test source folder of your project 6 that looks - depending on your MoreUnit settings - somewhat like this:

public class ClickHandlerTest {

  @Test
  public void testMarkMouseDown() {
    fail( "Not yet implemented" );
  }

  @Test
  public void testHandleMouseUp() {
    fail( "Not yet implemented" );
  }
}

The stubs may serve as starting point for populating the test case meaningfully from scratch. But mind that there is already a full coverage providing a set of tests in the ActionItemTest available. So another possibility is to move the content of the mouse click related tests to the newly created ClickHandlerTest. While the latter now does the thorough testing of the new unit, care has to be taken that there are enough tests left in the ActionItemTest that ensures a proper integration of the ClickHandler7.

Conclusion

Working like this for some time now, I am under the impression that maintainability and further development of my code (including test cases) gradually improves. It can be said of course, that MoreUnit should not be the crucial factor to decide whether or not to extract a certain responsibility into its own class. But to me it seems it eases the decision to do so, by helping to overcome one's weaker self, having an appropriate test case for the new class just a few clicks away.


  1. See red/green/refactor mantra of Test Driven Development
  2. Robert C. Martin, Clean Code, Chapter 10: Classes
  3. Although the example has been inspired by my current work I stripped it down to the bare minimum needed for this post
  4. A further refactoring step might be to change the ClickHandler in a way that it extends MouseAdapter or implements MouseListener. After that, the click handler itself could be registered as mouse listener at the label replacing the action item. This would eliminate the delegating mouse handler methods in ActionItem completely. However, I omit this step here to keep the post in scope
  5. This shortcut is usually used to switch between units under test and their according test cases. As you get used to this quite fast, it somehow serves even more as a 'missing test reminder' than the particular decorators do...
  6. With OSGi based systems this also might be a test fragment
  7. Note that the extraction process simplifies the test set up of the mouse click functionality tests. The extracted class has no dependency to an SWT widget derivative in the constructor, which allows to provide e.g. a test set up based on mocks. The latter is not possible without fuss for the ActionItem and therefore setup/teardown methods are needed to create, initialize and dispose of Display/Shell instances for example
Frank Appel
Follow me
Latest posts by Frank Appel (see all)
See also  Configure Your OSGi Services with Apache Felix File Install

6 Comments so far:

  1. Frank Neblung says:

    I never gave MoreUnit a try, because I can’t think of a class that is worth to be unit tested by a one-test-per-method approach. Clearly, finding responsibilities and placing them in their own classes is a good thing. But I don’t understand how MoreUnit helps in finding these responsibilities.

    • Frank Appel says:

      Frank,

      MoreUnit does not help in finding single responsibilities and I do not believe in a rigorous one-test-per-method approach either (usually I end up with more than one test per method anyway ;-))

      Nevertheless MoreUnit has some nice features that let me do my daily work more efficiently. However this efficiency is based on holding to a few conventions, like using a certain name-pattern relation between unit and unit-test or the like. But those conventions are not really unusual and do not collide in any way with my working habits (as I know that most of my TDD fellows use MoreUnit also, I guess the hurdle cannot be that high).

      Maybe the tool’s name suggests that its main purpose is the create-one-test-per-method-after-the-fact approach. Well, I do not know the vendor’s intention, but that is not how I am using it. And I really was not expecting writing more ‘units’. Because of this I was surprised that I apparently did. I think MoreUnit simply reduces test related handling burdon which might sometimes prevent lazy people like me from doing the rather obvious stuff.

      I mean it is not like ‘Oh, I probably should create a test case for this new class – well, let me select the related test package – open the new class wizard – type a name (what was the class name again?) – well let’s go back to the class and have a look which methods to test…’ but rather hit a few keys and the class is there having the right name located in the appropriate package and even having a few test stubs to start with if you like.

      To me this helps a lot, but of course everybody has to decide for oneself what makes his daily life easier

  2. Frank Neblung says:

    Thanks for your reply

    OK, I see benefit in easiliy creating and navigating to the respective test class.
    I should give it a try.

  3. The Alchemist says:

    Big fan of MoreUnit. Glad word is getting around…

  4. Dave Day says:

    Interesting, does moreunit work with Eclim?
    I program in several languages and prefer vim/eclim over Eclipse
    for my purposes.

    thanks,
    Dave

    • Frank Appel says:

      Dave,

      honestly, I have no clue. Maybe you should give it a try to find out? I would be happy to here about the result.

      Frank