|
Red Squirrel Reflections
Dave Hoover explores the psychology of software development
|
|
Mon, 24 Jan 2005Quote Manager Project -- Day 7 An ongoing pet project blog...
Must...write...test...for anonymous
Let's look at our
addButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
Source source = new Source(QuoteManagerPanel.this.source.getText(), page.getText(), isbn.getText());
Collection<Person> authors = new ArrayList<Person>();
for (int i = 0; i < NUMBER_OF_AUTHORS; i++)
if (authorPanels[i].hasText())
authors.add(new Person(authorPanels[i].getFirstName(), authorPanels[i].getLastName(), authorPanels[i].getUrl()));
Person quoter = new Person(quoterFirstName.getText(), quoterLastName.getText(), quoterUrl.getText());
new Quote(quoteText.getText(), source, authors, quoter);
}
});
I have reduced the number of objects the ActionListener depends on
by creating the AuthorPanel.
Looks like I could do something similar with the quoter fields.
Here we go again...how'd I start last time? Oh yeah, extract method...
private void layoutForm() {
GridBagConstraints constraints = new GridBagConstraints();
constraints.anchor = GridBagConstraints.WEST;
int y = 0;
addLabel("Quote", constraints, y++);
addField(quoteText, constraints);
addLabel("Source", constraints, y++);
addField(source, constraints);
addLabel("ISBN", constraints, y++);
addField(isbn, constraints);
addLabel("Page", constraints, y++);
addField(page, constraints);
constraints.gridwidth = GridBagConstraints.REMAINDER;
constraints.gridx = 0;
for (int i = 0; i < NUMBER_OF_AUTHORS; i++) {
constraints.gridy = y++;
add(authorPanels[i], constraints);
}
layoutQuoter(constraints, y);
y += 2;
constraints.anchor = GridBagConstraints.EAST;
constraints.gridx = 0;
constraints.gridwidth = GridBagConstraints.REMAINDER;
constraints.gridy = y++;
add(addButton, constraints);
}
private void layoutQuoter(GridBagConstraints constraints, int y) {
addLabel("Quoter", constraints, y);
addFullName(quoterFirstName, quoterLastName, constraints);
addLabel("URL", constraints, y+1);
addField(quoterUrl, constraints);
}
And then an inline method time four, and return a JPanel...
private void layoutForm() {
GridBagConstraints constraints = new GridBagConstraints();
constraints.anchor = GridBagConstraints.WEST;
int y = 0;
addLabel("Quote", constraints, y++);
addField(quoteText, constraints);
addLabel("Source", constraints, y++);
addField(source, constraints);
addLabel("ISBN", constraints, y++);
addField(isbn, constraints);
addLabel("Page", constraints, y++);
addField(page, constraints);
constraints.gridwidth = GridBagConstraints.REMAINDER;
constraints.gridx = 0;
for (int i = 0; i < NUMBER_OF_AUTHORS; i++) {
constraints.gridy = y++;
add(authorPanels[i], constraints);
}
constraints.gridy = y++;
add(layoutQuoter(), constraints);
constraints.anchor = GridBagConstraints.EAST;
constraints.gridx = 0;
constraints.gridwidth = GridBagConstraints.REMAINDER;
constraints.gridy = y++;
add(addButton, constraints);
}
private JPanel layoutQuoter() {
JPanel quoterPanel = new JPanel(new GridBagLayout());
GridBagConstraints constraints = new GridBagConstraints();
constraints.gridx = 0;
constraints.gridwidth = 1;
constraints.gridy = 0;
quoterPanel.add(new JLabel("Quoter"), constraints);
constraints.gridwidth = GridBagConstraints.RELATIVE;
constraints.gridx = 1;
quoterPanel.add(quoterFirstName, constraints);
constraints.gridwidth = GridBagConstraints.REMAINDER;
constraints.gridx = 2;
quoterPanel.add(quoterLastName, constraints);
constraints.gridx = 0;
constraints.gridwidth = 1;
constraints.gridy = 1;
quoterPanel.add(new JLabel("URL"), constraints);
constraints.gridwidth = GridBagConstraints.REMAINDER;
constraints.gridx = 1;
quoterPanel.add(quoterUrl, constraints);
return quoterPanel;
}
This looks very familiar, which is good, because it means I can probably share code with
the AuthorPanel. But not yet...first the inner class...
Now my anonymous
I have a
package com.redsquirrel.com.quotes.gui;
import org.jmock.MockObjectTestCase;
import org.jmock.Mock;
public class AddButtonActionListenerTest extends MockObjectTestCase {
public void testShouldProvideNewQuoteToManager() {
Mock manager = mock(Manager.class);
AddButtonActionListener actionListener = new AddButtonActionListener((Manager) manager.proxy());
actionListener.actionPerformed(null);
}
}
This test doesn't even come close to compiling, so I'll let IDEA do the work for me
with a series of wonderous F2/ALT-ENTER steps.
This leaves me with an empty Manager interface and a stubbed out
AddButtonActionListener. I'll run the test.
It passes. Doh! I didn't add any expectations! Here's a simple one...
public void testShouldProvideNewQuoteToManager() {
Mock manager = mock(Manager.class);
manager.expects(once()).method("newQuote").with(isA(Quote.class));
AddButtonActionListener actionListener = new AddButtonActionListener((Manager) manager.proxy());
actionListener.actionPerformed(null);
}
That fails. Good. Ahhh, gotta love that red bar.
Gives you a nice sense of purpose: get it to green ASAP.
Back to green with this ugly sin.
public class AddButtonActionListener implements ActionListener {
private final Manager manager;
public AddButtonActionListener(Manager manager) {
this.manager = manager;
}
public void actionPerformed(ActionEvent e) {
manager.newQuote(new Quote("foo", new Source("foo", "foo", "foo"), new HashSet
I'll make that test a bit more rigorous...
public void testShouldProvideNewQuoteToManager() {
Collection<Person> authors = new HashSet<Person>();
authors.add(new Person("Dee", "Hock", "http://chaordic.org/"));
String text = "Community is not about profit, it is about benefit. We confuse them at our peril.";
Source source = new Source("Birth of the Chaordic Age", "43", "1576750744");
Quote expected = new Quote(text, source, authors, null);
Mock manager = mock(Manager.class);
manager.expects(once()).method("newQuote").with(eq(expected));
AddButtonActionListener actionListener = new AddButtonActionListener((Manager) manager.proxy());
actionListener.actionPerformed(null);
}
That fails, and I'll leave it failing until tomorrow.
Here's my stop...
|