- Testing (and Punting) in Swing
- Frames: Making the Invisible Visible
- Constructing a Non-Renderable Frame
- Design and Testing Considerations
- Saving Time with a Quick Peek
- Where Do We Go from Here?
Frames: Making the Invisible Visible
The initial tests we create (see Listing 1) show that the HoldEm class should encapsulate a JFrame. The setUp code extracts the JFrame. The creation test verifies that the HoldEm class has properly initialized the frame. The second test, testShow, demonstrates that client code has control over setting visibility of the frame, and also that the frame defaults to invisible.
Listing 1 Starter tests for the GUI.
package ui; import javax.swing.*; import junit.framework.*; public class HoldEmTest extends TestCase { private HoldEm app; private JFrame frame; protected void setUp() { app = new HoldEm(); frame = app.frame(); } public void testCreate() { final double tolerance = 0.5; assertEquals(HoldEm.HEIGHT, frame.getSize().getHeight(), tolerance); assertEquals(HoldEm.WIDTH, frame.getSize().getWidth(), tolerance); assertEquals(JFrame.EXIT_ON_CLOSE, frame.getDefaultCloseOperation()); } public void testShow() { assertFalse(frame.isVisible()); app.show(); assertTrue(frame.isVisible()); } }
Production code to meet these two tests appears in Listing 2.
Listing 2 Initial HoldEm implementation.
package ui; import javax.swing.*; public class HoldEm { public static final int HEIGHT = 450; public static final int WIDTH = 600; private JFrame frame; public HoldEm() { initialize(); } private void initialize() { frame = new JFrame(); frame.setSize(WIDTH, HEIGHT); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); } public JFrame frame() { return frame; } public void show() { frame.setVisible(true); } }
If you run JUnit against your test suite, you’ll notice that a window actually appears for a brief instant and then disappears. Maybe this distraction is acceptable for one test out of 44 (the current unit test count). But as the application grows, and the number of windows displayed to the user grows, flashing JUnit tests will become a big annoyance and will slow down our tests. It’s best to head off this problem now, before it gets any worse.
We know that Sun’s JFrame setVisible method works. If we could prove that the HoldEm class did its job of calling the JFrame setVisible method with the correct argument, maybe that would suffice for our unit tests.
Remember that when we write unit tests, we’re trying to demonstrate that an object of a single class—a unit—behaves correctly. Usually, the easiest way to do that is to let it go about its business, whether or not that activity involves communicating with other objects.
Sometimes, however, those collaborations with other objects cause unit testing headaches. If we presume that those other classes are tested, and that everything else in our unit (the one we’re currently test-driving) is tested, the only remaining thing we need to prove is that our object interacts properly with those other classes. If we send a message to another object, we must demonstrate that the appropriate object received the message, along with any arguments. We must also demonstrate that our object does the right thing based on any information returned from the collaborators.
Specific to our case, we must prove that a HoldEm object calls the setVisible method on its JFrame, and we must prove that the HoldEm object passes the value true to this method.
In Java, there are a number of techniques we can use. Generally they involve taking advantage of polymorphism. We can send a message to an object by using its interface type (polymorphic dispatch means that we don’t really know which actual object receives the message).
Let’s start by creating a JFrame subclass that more or less ignores calls to setVisible (see Listing 3).
Listing 3 A modified definition for the JFrame.
package ui; import javax.swing.*; public class NonRenderableFrame extends JFrame { private boolean setVisible = false; @Override public void setVisible(boolean setVisible) { this.setVisible = setVisible; } @Override public boolean isVisible() { return setVisible; } }
We can then prepare our production class to allow the test to override the JFrame object used (see Listing 4).
Listing 4 Redesigning HoldEm to support testing.
package ui; import javax.swing.*; public class HoldEm { public static final int HEIGHT = 450; public static final int WIDTH = 600; private JFrame frame; public HoldEm() { initialize(); } private void initialize() { frame = createFrame(); frame.setSize(WIDTH, HEIGHT); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); } JFrame createFrame() { return new JFrame(); } public JFrame frame() { return frame; } public void show() { frame.setVisible(true); } }