OK, We Need a Test


We accept that we need customer tests. How can we do them? This is a GUI app: there s no obvious way to test it in an easy automated way. We could buy some screen-scraping software or something like that, but that seems wrong. Besides, we need a test right now.

Each of the customer stories for this application actually specifies a text translation that should take place and be shown in the GUI: User hits Enter. A new line containing P tags appears, after the end of the current line. And we have just built this TextModel object, which takes the input lines and a command and produces the output lines. So Chet and I thought we would do a little scripting language that allowed the customer (or us) to define customer tests in files: the input, the command, and the output. We thought a script might look like this:

 *input 
<P>Here is some input in the editor already.</P>
*end
*enter
*output
<P>Here is some input in the editor already.</P>

<P></P>

The idea is that a little script would define the input, all the lines between *input and *end . Then it would take various commands, like *enter , meaning user types Enter, and it would show what text should then be in the editor, from *output to the end of the script (or to *end , not shown here).

You might feel that the customer may not find this scripting language completely convincing. And you will be right. But they will almost certainly find it more convincing than nothing, and they ll find it far more repeatable than typing in the same old stuff into each new version. We ve found, again and again, that by producing some little language like this to make requirements concrete and check that they re implemented, we ve built a stronger relationship with our customer. And we ve found that the simple little idea soon grows into a stronger framework, but that instead of guessing what might be needed in the future and waiting until we have it, we get the real benefit right away. The bottom line is this: sb We don t need a testing framework; we need tests.

Having decided that we wanted this little script to run, we started by creating a new test file to contain this test and the customer tests that we were sure would follow. We called the file CustomerTest.cs, and the first test looked like this:

 [Test] public void ArrayInput() { 
String commands =
*input
some line
*end
*enter
*display
*output
some line

<P></P>";
InterpretCommands(commands);
}

As always, we start with something simple. Some text in the window, type Enter, and see the result. We made up a new command while we were at it, *display, that will display the text to the console. We were certain that when tests failed, as they surely would, we would want to see what the text looked like.

The InterpretCommands ? That s the method, as yet unwritten, that will go through the string commands line by line, setting up and executing the test. We wrote InterpretCommands this way:

 private void InterpretCommands(String commands) { 
StringReader reader = new StringReader(commands);
String line = reader.ReadLine();
while ( line != null) {
if ( line == "*enter")
model.Enter();
if (line == "*display")
Console.WriteLine("display\r\n{0}\r\nend", model.TestText);
if (line == "*output")
CompareOutput(reader);
if (line == "*input")
SetInput(reader);
line = reader.ReadLine();
}
}
Lesson  

For some readers, this is something you ve done many times. But for those who haven t done this sort of thing much, take a moment to look at how simple this command interpreter is. We don t need lex or yacc or some compiler generator. We don t need a powerful scripting framework. We just read lines, see whether they represent a command, and if they do, process the command. In this version, we re not even handling errors. When that starts to bother us, we ll improve the code.

The idea here, just as in our application, is to get going as soon as possible. We just wrote down our intention : if the line says *enter , tell the model to do Enter(). If the line says *input , set the input into the model. And so on.

Of course, we had to implement the SetInput() and CompareOutput() methods. They look like this, with the associated helper methods :

 private void SetInput(StringReader reader) { 
String[] input = ArrayToEnd(reader);
model.Lines = input;
}

private String[] ArrayToEnd(StringReader reader) {
ArrayList result = new ArrayList();
String line = reader.ReadLine();
while (line != null && line != "*end") {
result.Add(line.TrimEnd());
line = reader.ReadLine();
}
String[] answer = new String[result.Count];
result.CopyTo(answer);
return answer;
}

private void CompareOutput(StringReader reader, String message) {
String expected = ExpectedOutput(reader);
AssertEquals(message, expected, model.TestText);
}

private String ExpectedOutput(StringReader reader) {
return ReadToEnd(reader);
}

private String ReadToEnd(StringReader reader) {
String result = "";
String line = reader.ReadLine();
while (line != null && line != "*end") {
result += line;
result += System.Environment.NewLine;
line = reader.ReadLine();
}
return result;
}

In this code, you probably see some evidence that we re not very good yet with C#. That s part of the learning process in action. As the chapters wear on, you ll see that we use better techniques that we ve learned. In some cases, we ll go back and improve the old code. Usually we do that only when we come across something really awful , in the course of maintenance. If the code never needs changing, we re not as likely to go after it. As always, you have to find your own balance between keeping things clean and shipping useful code to your customer.




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