Begin with a Testsb


Begin with a Test sb

I m going well now, so I m not going to break the rhythm. I ll create a new Customer Acceptance Test and make it work. The only point of that test ”besides convincing the customer that things are working ”is to drive the implementation of the menu. Here s the new test and associated code:

 *altU 
*output
<UL>
<LI></LI>
</UL>

That should fail...and sure enough it does. So I ll add the menu item and its support. First the item:

 insertSection = new MenuItem ( 
"Insert &Section",
new EventHandler(MenuInsertSection));
insertPre = new MenuItem (
"Insert &Pre",
new EventHandler(MenuInsertPre));
insertUnorderedList = new MenuItem (
"Insert & UL",
new EventHandler(MenuInsertUnorderedList));
this.Menu = new MainMenu(new MenuItem[] {fileMenu, insertPre, insertSection,
insertUnorderedList } );

To support that, I need the MenuItem member variable, the new method MenuInsertUnorderedList, and the adjustment to the menu lookup code:

 class XMLNotepad : Form { 
public TestableTextBox textbox;
private TextModel model;
private MenuItem insertPre;
private MenuItem insertSection;
private MenuItem insertUnorderedList;
private MenuItem openFile;
private MenuItem saveFile;
private ModelAction enterAction;
private ModelAction shiftEnterAction;
private ModelAction insertSectionAction;
private ModelAction insertPreTagAction;
private ModelAction insertUnorderedListAction;
private ModelAction saveAction;
private ModelAction loadAction;
private String fileName;
private Boolean displayDialog = true;
public delegate void ModelAction();
public MenuItem MenuForAccelerator(string accelerator) {
if (accelerator == "&S") return insertSection;
if (accelerator == "&P") return insertPre;
if (accelerator == " & U") return insertUnorderedList;
if (accelerator == "^O") return openFile;
if (accelerator == "^S") return saveFile;
return null;
}

private void InitializeDelegates(TextModel model) {
enterAction = new ModelAction(model.Enter);
shiftEnterAction = new ModelAction(model.InsertReturn);
insertSectionAction = new ModelAction(model.InsertSectionTags);
insertPreTagAction = new ModelAction(model.InsertPreTag);
insertUnorderedListAction = new ModelAction(model.InsertUnorderedList);
saveAction = new ModelAction(this.SaveFile);
loadAction = new ModelAction(this.LoadFile);
}
void MenuInsertUnorderedList(object obj, EventArgs ea) {
CallModel(insertUnorderedListAction);
}

All this compiles, and I expect it to run. However, I forgot to implement the *altU command in the customer test. That happens almost every time, so I need to do something about that mistake. More importantly, I think, look at all the places I had to change or add code to add one menu item. As Andy Hunt would put it, the dog is biting me. We ll make this one work and then see what we can do to improve the situation. First, implement the *altU:

 private void InterpretCommands(String commands, String message) { 
StringReader reader = new StringReader(commands);
String line = reader.ReadLine();
CreateModel();
while ( line != null) {
if ( line == "*enter")
form.XMLKeyDownHandler((object) this, new KeyEventArgs(Keys.Enter));
if ( line == "*shiftEnter")
form.XMLKeyDownHandler((object) this,
new KeyEventArgs(Keys.Enter Keys.Shift));
if ( line == "*altS")
ExecuteMenu("&S");
if ( line == "*altP")
ExecuteMenu("&P");
if ( line == "*altU")
ExecuteMenu(" & U");
if (line == "*display")
Console.WriteLine("display\r\n{0}\r\nend", model.TestText);
if (line == "*output")
CompareOutput(reader, message);
if (line == "*input")
SetInput(reader);
if (line == "*loadfile")
LoadFile();
if (line == "*savefile")
SaveFile();
line = reader.ReadLine();
}
}

That does it! My tests all run. Now for some improvements. First, that irritating thing where the commands don t get implemented. I ll fix the InterpretCommands method to fail if it doesn t find the item. To begin with, I ll just create some else clauses so that I can do a final assert in the else. Or would a switch statement be better? I think I prefer the else approach because it s easier to get right: I always forget the break statements in switches. The C# compiler will detect that bug, but the code will be longer anyway. Let s do else:

 private void InterpretCommands(String commands, String message) { 
StringReader reader = new StringReader(commands);
String line = reader.ReadLine();
CreateModel();
while ( line != null) {
if ( line == "*enter")
form.XMLKeyDownHandler((object) this, new KeyEventArgs(Keys.Enter));
else if ( line == "*shiftEnter") form.XMLKeyDownHandler((object) this,
new KeyEventArgs(Keys.Enter Keys.Shift));
else if ( line == "*altS")
ExecuteMenu("&S");
else if ( line == "*altP")
ExecuteMenu("&P");
else if ( line == "*altU")
ExecuteMenu("&U");
else if (line == "*display")
Console.WriteLine("display\r\n{0}\r\nend", model.TestText);
else if (line == "*output")
CompareOutput(reader, message);
else if (line == "*input")
SetInput(reader);
else if (line == "*loadfile")
LoadFile();
else if (line == "*savefile")
SaveFile();
else
Assert(line + " command not defined", false);
line = reader.ReadLine();
}
}

That works fine. I tested it by commenting out the *altU line and running the tests. I don t see how to test it directly, since it s built to fail, and I m confident that once implemented, it will continue to work. We ll see if I regret that. So we have improved the world a little bit: forgetting to create a menu item will at least give us a clear message.

We d talked earlier about implementing a generic command, like *alt U , with a space, that would allow us to implement any Alt character. That might be valuable , but I m not feeling the pressure to do it, especially because this code is in a test, not in the real program. I m a bit more casual about keeping my tests squeaky clean than I am when dealing with the real code. I could be making a mistake here. For example, in the code just shown, I see some duplication. Notice all those pairs of lines that deal with *alt something. They all do exactly the same thing. OK, having noticed it, I guess I ll fix it:

 private void InterpretCommands(String commands, String message) { 
StringReader reader = new StringReader(commands);
String line = reader.ReadLine();
CreateModel();
while ( line != null) {
if ( line == "*enter")
form.XMLKeyDownHandler((object) this, new KeyEventArgs(Keys.Enter)); else if ( line == "*shiftEnter")
form.XMLKeyDownHandler((object) this,
new KeyEventArgs(Keys.Enter Keys.Shift));
else if (line.StartsWith("*alt"))
ExecuteMenu(" & "+line[4]);
else if (line == "*display")
Console.WriteLine("display\r\n{0}\r\nend", model.TestText);
else if (line == "*output")
CompareOutput(reader, message);
else if (line == "*input")
SetInput(reader);
else if (line == "*loadfile")
LoadFile();
else if (line == "*savefile")
SaveFile();
else
Assert(line + " command not defined", false);
line = reader.ReadLine();
}
}

That was easy, and it handles all the future alt commands automatically. The only thing I d like to do is move that new bit to the front of the ifs, because it s different from all the others:

 private void InterpretCommands(String commands, String message) { 
StringReader reader = new StringReader(commands);
String line = reader.ReadLine();
CreateModel();
while ( line != null) {
if (line.StartsWith("*alt"))
ExecuteMenu("&"+line[4]);
else if ( line == "*enter")
form.XMLKeyDownHandler((object) this, new KeyEventArgs(Keys.Enter));
else if ( line == "*shiftEnter")
form.XMLKeyDownHandler((object) this,
new KeyEventArgs(Keys.Enter Keys.Shift));
else if (line == "*display")
Console.WriteLine("display\r\n{0}\r\nend", model.TestText);
else if (line == "*output")
CompareOutput(reader, message);
else if (line == "*input")
SetInput(reader);
else if (line == "*loadfile")
LoadFile();
else if (line == "*savefile")
SaveFile();
else
Assert(line + " command not defined", false);
line = reader.ReadLine();
}
}

Good enough, that works. The structure here is suggesting that we need some kind of table, but so far I don t see a way to do it that would actually be better. Good for now. Let s recap, regroup, and take a break.

start sidebar
Lesson: How s It Going So Far?

We ve completed the first part of our story, the implementation of the unordered list. We have the list inserting OK, but we haven t done the part about adding a new LI instead of a new P when the user presses Enter inside the LI tags. We ll do that next . We have moved back and forth frequently between reading, testing, implementing, and cleaning up. I feel good about the way that has been going. The world is a little better place because of the cleanup, but we haven t spent large amounts of time avoiding useful work. On the other hand, we ve encountered a few places that we haven t cleaned up. The most notable is the fact that we have to make many changes, in many different places, to install a new menu item. That may take a little more time, and I d like to get this story completely done before addressing it. Now for that break!

end sidebar
 



Extreme Programming Adventures in C#
Javaв„ў EE 5 Tutorial, The (3rd Edition)
ISBN: 735619492
EAN: 2147483647
Year: 2006
Pages: 291

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net