Thursday, October 29, 2009

SAVE IN GUI









































Prev don't be afraid of buying books Next






























SAVE IN GUI






Provide, in the
GUI, the
capability to save the movie collection to the same file it was
previously saved to.





We took it slowly for the last task since there
were a few new ideas involved. Now we'll pick it up a bit since
this task requires only a refinement of what we've done so far on
this story. Specifically, we need to add the ability to resave to
the same file without asking the user for a filename again.






Test 56.
Telling the logical layer to "Save" causes it to save the current
list to the same file as the previous "Save As" operation.










Test 57.
Selecting "Save" from the "File" menu causes the list to be written
into the previously selected file.











Since the required setup for testing this
involves what we've already done for testing the Save As functionality, we'll extend the
testSaving() tests.





Test 56: Telling the logical layer to
"Save" causes it to save the current list to the same file as the
previous "Save As" operation



We'll start in
TestMovieListEditorFileOperations:




public void testSaving() throws Exception {
mockView.setMovies(movies);
control.setVoidCallable(1);
mockView.getFile();
control.setReturnValue(outputFile, 1);
mockView.getNameField();
control.setReturnValue(fotr.getName(), 1);
mockView.getCategoryField();
control.setReturnValue(fotr.getCategory(), 1);
mockView.getRatingField();
control.setReturnValue(fotr.getRating() + 1, 1);
mockView.setMovies(extendedMovies);
control.setVoidCallable(1);
control.activate();

MovieListEditor editor = new MovieListEditor(movieList, mockView);
assertTrue("Editor should have saved",editor.saveAs());

FileAssert.assertSize("Save As-ed file has wrong size.",
expected.length(),
outputFile);

FileAssert.assertEquals("Save As-ed file has wrong contents ",
expected,
outputFile);

editor.add();
assertTrue("Editor should have resaved", editor.save());
FileAssert.assertEquals("Saved file ", extendedExpected, outputFile);
control.verify();
}






Here we've added to the test to add a movie to
the list, resave it, and verify the result. To support this, we
need to extend the fixture:




fotr = new Movie("The Fellowship of The Ring", Category.FANTASY, 5);
extendedExpected = expected + "The Fellowship of The Ring|Fantasy|5\n";
extendedMovies = new Vector(movies);
extendedMovies.add(fotr);






Add the required stub for
MovieListEditor.save() and give it a try:




public booleansave() {
return false;
}






Of course it fails because save()
doesn't do anything yet. Most notably, it just returns
false. We need to write some code for save():




public booleansave() {
if (outputFile == null) {
return false;
}

FileWriter writer = new FileWriter(outputFile);
movies.writeTo(writer);
writer.close();
return true;
}






To make this compile we need to have an instance
variable for outputFile. Add it, compile, and run the
tests. It still fails because we don't initialize the
outputFile instance variable. We'll do this in
saveAs(); we need to use the new instance variable rather
than the local we had before:




public boolean saveAs() throwsIOException {
outputFile = view.getFile();
if (outputFile == null) {
return false;
}

FileWriter writer = new FileWriter(outputFile);
movies.writeTo(writer);
writer.close();
return true;
}






Now the test fails because the resulting file is
wrong. Hmm. . . that seems unlikely. Everything looks good. Run the
test in the debugger (set a breakpoint on entry to
MovieListEditor.save()) and we find that the
Movie that got added is uncategorized and unrated. Hmm. .
. a quick look at MovieListEditor.add() reveals the
problem. It was creating a movie with just a name. Ponder, ponder,
scratch, scratch. . . oh, yes. Our thought was to create a template
movie to probe for a duplicate. We missed going back and filling in
the rest of the data. A quick look at the test for adding (which
was written when we had only a name) shows that the mock has no
expectations regarding the category and rating being fetched during
an add. Not a nice feeling, discovering that—but it happens.
Because we are practicing TDD, we have a better chance of finding
that sort of thing early.



First things first, though. We'll fix
add() to get our current test passing:




public void add() {
String newName = view.getNameField();
Movie newMovie = new Movie(newName,
view.getCategoryField(),
view.getRatingField());
try {
movies.add(newMovie);
updateMovieList();
} catch (DuplicateMovieException e) {
view.duplicateException(newName);
}
}






Rerun the test. Green. It's not clean yet.





Exercises












15.

Fix the add() related tests in
TestMovieListEditor.












Now to clean up MovieListEditor. If we
compare save() and saveAs() we see that other
than the initial fetch of outputFile in saveAs(),
they are identical. That's an easy cleanup: we just have to have
saveAs() set outputFile and call
save():




public boolean saveAs() throwsIOException {
outputFile = view.getFile();
return save();
}






Green and clean!





Test 57: Selecting "Save" from the
"File" menu causes the list to be written into the previously
selected file



The next step is to do something very similar to
the GUI testSaving(). But first we'll do some damage
control and fix up the add-related GUI tests which we see are also
out of date. Actually, I'll make that an exercise for you.





Exercises












16.

Fix up the add related tests in
TestSwingMovieListEditorView.












Here's the new testSaving() GUI
test:




public void testSaving() throws Exception {
JMenuBarOperator menubar = new JMenuBarOperator(mainWindow);
JMenuOperator fileMenu = new JMenuOperator(menubar, "File");
fileMenu.push();

JMenuItemOperator saveAsItem =
new JMenuItemOperator(mainWindow,
new NameBasedChooser("saveas"));
saveAsItem.pushNoBlock();
JFileChooserOperator fileChooser = new JFileChooserOperator();
fileChooser.setSelectedFile(outputFile);
JButtonOperator saveButton = new JButtonOperator(fileChooser, "Save");
saveButton.push();
FileAssert.assertSize("Save-Ased list has wrong size.",
savedText.length(),
outputFile);
FileAssert.assertEquals("Save-Ased file", savedText, outputFile);

JTextFieldOperator newMovieField =
new JTextFieldOperator(mainWindow,
new NameBasedChooser("name"));
newMovieField.enterText(theShining.getName());

JComboBoxOperator ratingCombo =
new JComboBoxOperator(mainWindow,
new NameBasedChooser("rating"));
ratingCombo.setSelectedIndex(theShining.getRating() + 1);

JComboBoxOperator categoryCombo =
new JComboBoxOperator(mainWindow,
new NameBasedChooser("category"));
categoryCombo.setSelectedIndex(2);

JButtonOperator addButton =
new JButtonOperator(mainWindow,
new NameBasedChooser("add"));
addButton.doClick();

fileMenu.push();
JMenuItemOperator saveItem =
new JMenuItemOperator(mainWindow,
new NameBasedChooser("save"));
saveItem.push();

FileAssert.assertSize("Saved list has wrong size.",
extendedSavedText.length(),
outputFile);

FileAssert.assertEquals("Saved file",
extendedSavedText,
outputFile);
}






We need to also extend the fixture somewhat:




theShining = new Movie("The Shining", Category.HORROR, 2);
extendedSavedText = savedText + "The Shining|Horror|2\n";






This compiles but fails because there is no
Save item. Easy enough to add:




priate JMenuBar initMenuBar() {
JMenuBar menuBar = new JMenuBar();
JMenu fileMenu = new JMenu("File");
menuBar.add(fileMenu);

JMenuItem saveAsItem = new JMenuItem("Save As.. .");
saveAsItem.setName("saveas");
saveAsItem.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
try {
myEditor.saveAs();
} catch (IOException ex) {
// TODO: deal with this
}
}});
fileMenu.add(saveAsItem);

JMenuItem saveItem = new JMenuItem("Save");
saveItem.setName("save");
fileMenu.add(saveItem);
return menuBar;
}






Now the test fails because the file is not being
updated. We next need to call save() in the associated
MovieListEditor in response to the Save item being selected:




private JMenuBar initMenuBar() {
JMenuBar menuBar = new JMenuBar();
JMenu fileMenu = new JMenu("File");
menuBar.add(fileMenu);

JMenuItem saveAsItem = new JMenuItem("Save As.. .");
saveAsItem.setName("saveas");
saveAsItem.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
try {
myEditor.saveAs();
} catch (IOException ex) {
// TODO: deal with this
}
}});
fileMenu.add(saveAsItem);

JMenuItem saveItem = new JMenuItem("Save");
saveItem.setName("save");
saveItem.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
try {
myEditor.save();
} catch (IOException ex) {
// TODO: deal with this
}
}});
fileMenu.add(saveItem);
return menuBar;
}






The test now runs green. However,
initMenuBar has grown too large and is starting to smell.
A couple of Extract Method applications (one to each
JMenuItem setup) will take care of it:




private JMenuBar initMenuBar() {
JMenuBar menuBar = new JMenuBar();
JMenu fileMenu = new JMenu("File");
menuBar.add(fileMenu);
fileMenu.add(initSaveAsItem());
fileMenu.add(initSaveItem());
return menuBar;
}

private JMenuItem initSaveAsItem() {
JMenuItem saveAsItem = new JMenuItem("Save As. . .");
saveAsItem.setName("saveas");
saveAsItem.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
try {
myEditor.saveAs();
} catch (IOException ex) {
// TODO: deal with this
}
}});
return saveAsItem;
}

private JMenuItem initSaveItem() {
JMenuItem saveItem = new JMenuItem("Save");
saveItem.setName("save");
saveItem.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
try {
myEditor.save();
} catch (IOException ex) {
// TODO: deal with this
}
}});
return saveItem;
}






OK. Green and clean! There's still some
duplication in the menu item creation and setup code, but that's
largely unavoidable in GUI construction code where you are creating
multiples of the same type of component.















































Amazon






No comments:

Post a Comment