BRUTE FORCE
The first approach we will explore is to give our tests direct access to the visual components in the GUI. We can do this in one of several ways:
make them public instance variables so that the test can access them directly
provide an accessor for each component so that the test can get hold of them
make the test class an inner class of the class that creates the GUI
use reflection to gain access to the components
All of these approaches have the drawback that the components cannot be local to a method; they must be instance variables. Also, all but the last require extra production code that is used only for testing.
Since I learned Object Oriented Programming in a Smalltalk environment, I find the idea of making instance variables public unthinkable. This rules out option one. Also, I like to keep my test code separate from production code, so I avoid option three as well. The last option is very overhead intensive to do manually. We'll explore some frameworks later in this chapter that make use of reflection, but hide the details.
That leaves the second approach: adding accessors for the components.
Keep in mind that the code was developed incrementally: enough new test to fail, followed by enough new code to pass. During development, both the code and the tests were refactored as needed. Notice how the tests are in two TestCase classes. Notice the different setUp() methods.
|
As we discuss more fully in the JUnit chapter, TestCase is a mechanism to group tests that rely on the same fixture, not tests that happen to use the same class. When we need a different fixture, we should create a new TestCase class. As you add tests, add them to the TestCase that maintains the fixture that they require.
|
Given that preamble, here's the test code. First we have a TestCase that verifies that the required components are present. Notice how we've used EasyMock. For this test we aren't concerned with the interaction of the GUI with the underlying object (i.e., the mock) so we've used niceControlFor() which provides default implementations for all of the methods in MovieListEditor.
public class TestWidgets extends TestCase { private MockControl control; private MovieListEditor mockEditor; private MovieListWindow window;
protected void setUp() { control = EasyMock.niceControlFor(MovieListEditor.class); mockEditor = (MovieListEditor) control.getMock(); control.activate(); window = new MovieListWindow(mockEditor); window.init(); window.show(); }
public void testList() { JList movieList = window.getMovieList(); assertNotNull("Movie list should be non null",movieList); assertTrue("Movie list should be showing",movieList.isShowing()); }
public void testField() { JTextField movieField = window.getMovieField(); assertNotNull("Movie field should be non null",movieField); assertTrue("Movie field should be showing",movieField.isShowing()); }
public void testAddButton() { JButton addButton = window.getAddButton(); assertNotNull("Add button should be non null",addButton); assertTrue("Add button should be showing",addButton.isShowing()); assertEquals("Add button should be labeled \"Add\"", "Add", addButton.getText()); }
public void testDeleteButton() { JButton deleteButton = window.getDeleteButton(); assertNotNull("Delete button should be non null",deleteButton); assertTrue("Delete button should be showing",deleteButton.isShowing()); assertEquals("Delete button should be labeled \"Delete\"", "Delete", deleteButton.getText()); } }
The other TestCase tests for correct operation of the GUI and uses a more involved mock. Here we build the mock in each test method, setting different method call expectations and return values in each. Note, however, the common mock creation code in setUp().
public class TestOperation extends TestCase { private static final String LOST IN SPACE = "Lost In Space"; private Vector movieNames; private MovieListWindow window; private MockControl control=null; private MovieListEditor mockEditor = null;
protected void setUp() { movieNames = new Vector() { { add("Star Wars"); add("Star Trek"); add("Stargate"); } }; window = null;
MockControl control = EasyMock.controlFor(MovieListEditor.class); MovieListEditor mockEditor = (MovieListEditor) control.getMock(); }
public void testMovieList() { mockEditor.getMovies(); control.setReturnValue(movieNames, 1);
control.activate();
MovieListWindow window = new MovieListWindow(mockEditor); window.init(); window.show();
JList movieList = window.getMovieList(); ListModel movieListModel = movieList.getModel(); assertEquals("Movie list is the wrong size", movieNames.size(), movieListModel.getSize());
for (int i = 0; i < movieNames.size(); i++) { assertEquals("Movie list contains bad name", movieNames.get(i), movieListModel.getElementAt(i)); }
control.verify(); }
public void testAdd() { Vector movieNamesWithAddition = new Vector(movieNames); movieNamesWithAddition.add(LOST IN SPACE);
mockEditor.getMovies(); control.setReturnValue(movieNames, 1);
mockEditor.add(LOST IN SPACE); control.setVoidCallable(1);
mockEditor.getMovies(); control.setReturnValue(movieNamesWithAddition, 1);
control.activate();
MovieListWindow window = new MovieListWindow(mockEditor); window.init(); window.show();
JTextField movieField = window.getMovieField(); movieField.setText(LOST IN SPACE);
JButton addButton = window.getAddButton(); addButton.doClick();
JList movieList = window.getMovieList(); ListModel movieListModel = movieList.getModel(); assertEquals("Movie list is the wrong size after add", movieNamesWithAddition.size(), movieListModel.getSize());
assertEquals("Movie list doesn't contain new name", LOST IN SPACE, movieListModel.getElementAt(movieNames.size()));
control.verify(); }
public void testDelete() { Vector movieNamesWithDeletion = new Vector(movieNames); movieNamesWithDeletion.remove(1);
mockEditor.getMovies(); control.setReturnValue(movieNames, 1);
mockEditor.delete(1); control.setVoidCallable(1);
mockEditor.getMovies(); control.setReturnValue(movieNamesWithDeletion, 1);
control.activate();
MovieListWindow window = new MovieListWindow(mockEditor); window.init(); window.show();
JList movieList = window.getMovieList(); movieList.setSelectedIndex(1);
JButton deleteButton = window.getDeleteButton(); deleteButton.doClick();
ListModel movieListModel = movieList.getModel(); assertEquals("Movie list is the wrong size after delete", movieNamesWithDeletion.size(), movieListModel.getSize());
control.verify(); } }
The GUI class is below, with a screen shot of the resulting window shown in Figure 8.2.
public class MovieListWindow extends JFrame { private JList movieList; private JButton addButton; private MovieListEditor myEditor; private JTextField movieField; private JButton deleteButton;
public MovieListWindow(MovieListEditor anEditor) { super(); myEditor = anEditor; }
public JList getMovieList() { return movieList; }
public JTextField getMovieField() { return movieField; }
public JButton getAddButton() { return addButton; }
public JButton getDeleteButton() { return deleteButton; }
public void init() { setLayout(); initMovieList(); initMovieField(); initAddButton(); initDeleteButton(); pack(); }
private void setLayout() { getContentPane().setLayout(new FlowLayout()); }
private void initMovieList() { movieList = new JList(getMovies()); JScrollPane scroller = new JScrollPane(movieList); getContentPane().add(scroller); }
private void initMovieField() { movieField = new JTextField(16); getContentPane().add(movieField); }
private void initAddButton() { addButton = new JButton("Add"); addButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { myEditor.add(movieField.getText()); movieList.setListData(getMovies()); } }); getContentPane().add(addButton); }
private void initDeleteButton() { deleteButton = new JButton("Delete"); deleteButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { myEditor.delete(movieList.getSelectedIndex()); movieList.setListData(getMovies()); } }); getContentPane().add(deleteButton); }
private Vector getMovies() { Vector movies = myEditor.getMovies(); return (movies == null) ? new Vector() : movies; } }
|
No comments:
Post a Comment