JOptionPane Popping Up During Unit Tests

This story is the story of good old fashioned decoupling, and an example of Java’s Bridge and Adapter patterns.

My client has had a piece of code that for years, yes years, was popping up a java Swing JOptionPane message dialog during unit tests runs in Eclipse (via a Maven plugin) and Eclipse’s JUnit runner.  The surprising thing is that all the developers would tolerate this . . . and all the developers would run their builds inside of Eclipse.

The codebase sits on SVN, and I’ve been running git-svn infront of it and using the command line quite a bit.  My builds have all been terminal-based Maven or a build script also run from the terminal. For whatever reason I couldn’t pin down — maybe a global suppress warnings — I wasn’t getting the dialogs.  But I *was* aware of the problem because I make sure my stuff runs in Eclipse as well. So I logged it in my Kanboard database and came back to it.

The solution was a bit simple, just bury JOptionPane in another interface layer.   I had thought about it, then researched soloutions regarding Swing component testing (seriously how many fat clients do we Java people write these days?).  Lot’s of static methods and I haven’t finished dropping in PowerMock and its module for Mockito yet. I followed the bridge/adapter path written up by Shervin Asgari and thank him.  The technique is fundamental enough, and overlooked enough, that I feel compelled to write it up here.

Inside some of our service code is this pop-up for an error:


//Service code
public void codeMethod() {
   try {
      //...something and maybe error
   } catch (Exception e) {
      LOG.error("Service exception: " + e.getMessage(), e);
      JOptionPane.showMessageDialog(null, "Service is broken.  Contact help support", "", 
      JOptionPane.ERROR_MESSAGE);
   }
)

Again the problem is, when running JUnit tests via Maven *in* Eclipse or via the Junit runner, the pop-up shows and requires response. We don’t want this for obvious reasons.

Unfortunately this Swing widget has a lot of static methods so we can’t simply extend the class with an interface and mock that interface. The solution instead is to make an separate interface and implementation, calling the JOptionPane methods.

First I make an interface and a concrete implementation, the latter which executes the JOptionPane method:


public interface OptionPane {
   public void showMessageDialog(Component parentComponent, Object message, 
   String title, int messageType);
}

public class OptionPaneImpl implements OptionPane {
   public OptionPaneImpl() {
      //intentional
   }
   /*** we have moved the code from the service to here ***/
   public void showMessageDialog(Component parentComponent, Object message, 
   String title, int messageType) {
      JOptionPane.showMessageDialog(parentComponent,message,title,messageType);
   }
}

Now we put the code into the service, and we are ready for mock testing:

//Service code
private OptionPane optionPane = new OptionPaneImpl();

public void codeMethod() {
   try {
      //...something and maybe error
   } catch (Exception e) {
      LOG.error("Service exception: " + e.getMessage(), e);
      optionPane.showMessageDialog(null, "Service is broken.  Contact help support", "", 
      JOptionPane.ERROR_MESSAGE); //<-- swapped the interface into here
   }
)

Here’s what the test looks like with JUnit/Mockito:


public class ServiceCodeTest
{
   @InjectMocks
   private ServiceCode serviceCode;

   @Mock
   private OptionPane mockOptionPane;

   @Before
   public void setUp() {
      MockitoAnnotations.initMocks(this);
   }

   @Test
   public void testDoCalculate() throws Exception
  {
     //comment out mock declaration and following stub to see message dialog
     doNothing().when(mockOptionPane).showMessageDialog(any(Component.class), 
     anyObject(), anyString(), anyInt());

     serviceCode.doCodeMethod();
  }
}

There almost never seems to be a reason *not* to use an interface. At worse you just write a few extra lines of code but the decoupling, extendability and testability are invaluable.

, , , , , ,

Leave a Reply

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>