- Adding Behaviors to the View
- Getting the Test to Pass
- What About Players?
- Showing the Hole Cards
- Whats Next?
Getting the Test to Pass
In order to get the test to pass, we first need to store a reference to the Deal button in a field. We also want to rename it from the generic name button to dealButton, a name that makes more sense in the larger context of the class. Once we get those changes to pass, we can add the method addDealListener. Its implementation is simple—all it does is delegate to the JButton method addActionListener. Listing 2 shows the complete set of code changes to TablePanel.
Listing 2 View changes.
public class TablePanel extends JPanel { ... private JButton dealButton; ... private void initialize() { dealButton = new JButton(Bundle.get(DEAL_BUTTON)); dealButton.setName(DEAL_BUTTON); add(dealButton); ... } ... public void addDealListener(ActionListener listener) { dealButton.addActionListener(listener); } }
We’ve proven that our view supports pluggable actions, but we haven’t defined yet what the correct action is for our Texas Hold ’Em application. We’ll need code that tells the view to start dealing cards when the user clicks the Deal button. This code will be part of an application class.
Before proceeding, I notice that the HoldEm class is inappropriately named. It’s really a view component, even though it has the ability to be executed as an application from the command line. Let’s rename it to HoldEmFrame, and rename the test to HoldEmFrameTest. Looking at HoldEmFrameTest, we should also rename the HoldEmFrame instance variable app:
public class HoldEmFrameTest extends TestCase { private HoldEmFrame holdEmFrame; private JFrame frame; ...
We’d rather use the simple field name frame, but that’s already taken. There’s probably a better pair of names, but the important thing is that we improved upon a poor name. We can always revisit the name next time we have to touch the code.
Let’s start by building a simple test that creates an application class. Listing 3 shows the test, and Listing 4 shows the application class HoldEmApp.
Listing 3 Creating the application.
package ui; import junit.framework.*; public class HoldEmAppTest extends TestCase { public void testRun() { HoldEmApp app = new HoldEmApp(); app.run(); HoldEmFrame holdEmFrame = app.getFrame(); assertTrue(holdEmFrame.frame().isVisible()); } }
Listing 4 Initial implementation of the application.
package ui; public class HoldEmApp { private HoldEmFrame frame; public void run() { frame = new HoldEmFrame(); frame.show(); } HoldEmFrame getFrame() { return frame; } }
The primary goal of testRun is to verify that the actual frame is displayed when we run the application. I don’t like having to obtain a HoldEmFrame object so that we can ask for its underlying JFrame. We have two options:
- Change HoldEmFrame so that it’s a JFrame.
- Fake the show method on HoldEmFrame and verify that it gets called.
Neither of these options is perfect; technically, both testing forms break encapsulation. For now, we’ll choose the non-mock solution. It seems simpler to just execute code and see what effects propagate. We’ll use this as a guiding principle going forward, although I’m sure that we’ll have to resort to mocking at some point.
We can drive the design change through the unit test. Step 1, we insist that HoldEmFrame is a JFrame:
public void testCreate() { assertTrue(JFrame.class.isAssignableFrom(HoldEmFrame.class)); ...
We can then simplify testRun—it no longer needs to extract a JFrame reference from the HoldEmFrame object. Here are some succinct steps for doing this:
- Rename the HoldEmFrame field from holdEmFrame to
frame. This duplicates the name of the JFrame reference. In Eclipse,
make this change using a local rename refactoring (Ctrl+2, R):
private HoldEmFrame frame; private JFrame frame; // this won’t compile!
- Remove the JFrame field declaration:
private JFrame frame; // delete this
- Remove the line in setUp that previously was assigned to the JFrame
reference:
frame = frame.frame(); // delete this
Then it’s a simple matter of correcting code in the HoldEmFrame class. Oops, not so simple. Two problems:
- show is defined as a deprecated method in JFrame. We’ll change that to display instead.
- Our mock definition in HoldEmFrameTest no longer works—it’s tied to the fact that HoldEmFrame was giving out its encapsulated JFrame reference.
Listing 5 shows the fixed mock definition.
Listing 5 A corrected mock for HoldEmFrame, coded in HoldEmFrameTest.
protected void setUp() { frame = new HoldEmFrame() { private boolean setVisible = false; @Override public void setVisible(boolean setVisible) { this.setVisible = setVisible; } @Override public boolean isVisible() { return setVisible; } }; }
The corrected version of HoldEmFrame is shown in Listing 6. We no longer need to reference the encapsulated JFrame.
Listing 6 HoldEmFrame.
package ui; import javax.swing.*; import util.*; public class HoldEmFrame extends JFrame { public static final int HEIGHT = 450; public static final int WIDTH = 600; static final String TITLE = "holdem.title"; public static final int SEATS = 10; public HoldEmFrame() { initialize(); } private void initialize() { setTitle(Bundle.get(HoldEmFrame.TITLE)); setSize(WIDTH, HEIGHT); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); getContentPane().add(new TablePanel(SEATS)); } public void display() { setVisible(true); } }
Finally, back in HoldEmAppTest, we can simplify our assertion:
assertTrue(holdEmFrame.isVisible());