Crafting Java with Test-Driven Development, Part 11: Making Things Happen

Our poker application shows a simple view with a "deal" button. The only problem is that the button does nothing when clicked! We'll want to deal cards when a user presses the button, but how do we accomplish that in a test-first way? As usual, Jeff Langr has the answers.

From the author of

From the author of

In the last installment, we built a view class that shows a list of players
and their positions around the table. It also shows a button labeled Deal that
does nothing. It’s not a pretty interface, but it’ll suffice for
now. Later we can hire a Swing layout expert to make things look nice.

We want the user interface to support doing something when a user
clicks the Deal button. What should that something be? Well, we don’t know
yet, and honestly, we don’t care yet. Our job right now is to finish
development of the view class TablePanel.

Adding Behaviors to the View

Dealing with the button click is technically the job of what’s termed
the controller. In a classic user interface design, the controller
receives events from input devices (including the keyboard and mouse), and
delegates these messages to the underlying model as appropriate. The design of
Swing typically promotes combining controller and view responsibilities in the
same class.

In Swing, you make a button effect behavior by attaching an
ActionListener object to it. You implement the behavior in the
ActionListener callback method actionPerformed. Usually, when
programmers build a Swing class, they attach ActionListener specifics
to a button at the same time as they incorporate the button into the view.

Our job in building the TablePanel class is to create a class that
can display a view and also receive controller input from that view. Neither of
these responsibilities should have to refer to underlying business logic.
Instead, changes to the view will be driven by application code, and events from
the controller will be broadcast to any application code that cares to
listen.

Our TablePanelTest is a client of TablePanel; in fact,
it’s the first client. Think of it as providing an example of how a client
should interact with a TablePanel object. Our test method,
testDeal, shows how a client needs to tell the view to attach a
specific behavior to the Deal button (see Listing 1).

The behavior we attach to a TablePanel in testDeal serves
the need of testing. This client code simply tracks whether the
actionPerformed behavior was executed. Here are the steps we take in
testDeal:

Create a button.

Initialize wasClicked to false.

Tell the TablePanel object to attach the testing callback.

Emulate pressing the button by calling doClick.

Assert that wasClicked evaluates to true; that is, that
our callback actually was executed.

What we’re doing here is providing mock behavior for the purposes of
being able to write a test. The code in the ActionListener definition
that we pass to the TablePanel object isn’t what our production
listener code will look like. But this listener allows us to design an
independent TablePanel class. We’re unit testing in a strict
sense—using tests to drive the design of a class so that it can act as a
standalone unit.