Javaв„ў EE 5 Tutorial, The (3rd Edition)

First, I want to find out whether this idea will work. I ll write a test that will get a MenuItem from the Form, click it, and see what happens. Let s look for a place to put that test. The CustomerTest class has the right setup now, since it already has the Form and model hooked together. I ll just add a new direct test to that class for now, expecting to remove it later, when all the wiring is hooked up:

[Test] public void DirectMenu() { form.InsertPreMenu().PerformClick(); AssertEquals("<pre></pre>\r\n", model.TestText); }

Something like that should do the trick. I just assumed a new method on the XML Notepad: InsertPreMenu(), which will return the menu. Let s build that. In the initialize, we have this:

MenuItem insertPre = new MenuItem ( "Insert &Pre", new EventHandler(MenuInsertPre));

I ll promote the insertPre variable to a member and write the accessor. (I could make it a public member and access it directly, but I usually don t. And I m thinking that although my test would run a bit sooner, that structure is closer to where I m going. A judgment call ”I almost went the other way.)

private MenuItem insertPre; public MenuItem InsertPreMenu() { return insertPre; }

OK, that all compiles. Now to test...whoa! The DirectEnter worked ”that s cool ”but the TestAllFiles broke. It must have been at red bar before I started this. Now what?

Lesson  

This is an important topic. Whenever we leave the code ” and there s an overnight between the previous section and this one ” we should leave the tests at green bar in most cases. There can be a case made for leaving one test written but not coded, as a reminder of what to do next , but I think it s best to have the system working at all times rather than broken. So it s good that this happened , because it lets me make the point. I am truly confused at this moment. I recognize the error message, something about TestAllFiles: ˜-1 is not a valid value for ˜value . That s cryptic. The stack trace says it s in PutText, SetSelectionStart, so there must be a -1 in there somehow.

It s irritating that TestAllFiles doesn t say which test failed. As a quick fix, I ll have it just print the file name to the Console. The last one printed will be the problem. It s sect1.test, the test for the InsertSectionTags capability...that sends Alt+S...oh, right! I was trying to send the Alt+S keystroke to the Form, and it didn t work. That s how we got here. I ll change that line back to doing AltS(), and we should be back on track. Right, OK.

Lesson  

Now that could have turned into trouble. Even now I m kind of thrown off the track, and if it had been hard to find the bug I would have lost momentum. OK, important safety tip: sb keep the tests green. (Thanks, Egon.)

Back to the main plan. The test of PerformClick() on the returned menu item worked. So if the test gets the right menu item, it can send PerformClick() and go on with the testing. Therefore, the code that now says

if ( line == "*altS") model.AltS();

can be changed to do our new *menu idea or something similar. I m tempted to take a big bite here, but it s early in the morning and I m going to go very slowly.

if ( line == "*altS") ExecuteMenu("&S");

I m imagining a new method, ExecuteMenu(), that will look up a menu item based on its accelerator, which I ll be pulling off the *menu line shortly. I code that as follows :

private void ExecuteMenu(string accelerator) { form.MenuForAccelerator(accelerator).PerformClick(); }

Here I m assuming that the Form will look up the menu item based on its accelerator and hand it back. I ll just implement that method by using the InsertPreMenu() that I already wrote. I ll change its name, let the other test fail, and then change it to use ExecuteMenu also.

public MenuItem MenuForAccelerator(string accelerator) { return insertPre; } [Test] public void DirectMenu() { form.MenuForAccelerator("&S").PerformClick(); AssertEquals("<pre></pre>\r\n", model.TestText); }

I expect this to compile and go green...and it all does, except that the sect1 test fails again. This time the message is new: the output is <pre></pre>. That s what I get for carrying on a business conversation, writing this chapter, and programming all at the same time. MenuForAccelerator returns only that one menu item, so I m getting the wrong one for Alt+S. And in the DirectMenu, that string should have been &P, not S. Why didn t my pair catch that? I ll fix that, and I ll do a quick hack to MenuForAccelerator. Then we ll discuss it. Here s the code:

private MenuItem insertPre; private MenuItem insertSection; public MenuItem MenuForAccelerator(string accelerator) { if (accelerator == " & S") return insertSection; return insertPre; }

Yes! Tests all green. One more change because I m hot. I m going to let the customer tests do altP just like altS. Then we ll talk about that hack just above:

if ( line == "*altS") ExecuteMenu("&S"); if ( line == "*altP") ExecuteMenu("&P");

OK, tests all green. Now what s up with that obviously dumb implementation of MenuForAccelerator? That is clearly not going to last. Here s my thinking:

Lesson  

When I ve got a test on red bar, I want to get to green as quickly as possible. I think of how you and I might build a bridge across a small stream. First thing we might do would be put a line of boards across the stream to stand on. Then, using those boards as a scaffold , we d build up the bridge bit by bit. I m not sure how well that would work on large-scale bridges, but in software it works just fine.

Look at the structure of the program for a moment. The CustomerTest command interpreter is actually making an appropriate call to the Form, getting the right menu, clicking it, and checking the results. It s not doing it very generally compared to the *menu idea, but it is basically doing the right thing ”just not in the best possible way. And in the Form, the code is looking up the correct MenuItem and returning it. Again, not a general solution, but it is actually doing it. That s the most important aspect of things: we have the program in the right shape, doing the right things. Now we have a few things to make better. That will be a bit time-consuming but not risky; all the hard parts are now laid in. Best of all, our tests are now better, and they are all green.

Lesson  

There s an old saying in programming: sb Make it work, make it right, make it fast. Get the code into the right overall shape quickly: make it work. Then, as the need for a more general solution shows up, improve and refactor the code: make it right. Finally, if and only if a performance measurement shows that you need to, make it fast.

We need to change the Form to save the menu items in a list of some kind, maybe a hashtable, and return them based on their accelerator. We need to deal with the likelihood that the one we re looking for won t be found. Maybe we ll return a null and check for it. And we need to change the Customer Acceptance Test scripting language to have the *menu command, so that we don t have to add code to the CustomerTest class every time the customers ask for a new menu item.

I ll work on those in the next chapter, which should be pretty short unless something else exciting happens. I ll display some big chunks of code at that time so that you can get the big picture if you want it.

Lesson: Let s Sum Up

First and most important, I think, is that a small effort has resulted in a large improvement in the quality of our Customer Acceptance Tests. They are actually going through the keystroke and menu logic of the Form now, such that more of the code is checked. Our customer should feel more confident now after running the tests.

I was ready to give up on this. On a real project, I think I might have. I pushed forward only because this is a book and I feel that I have to let you see how things turn out when we do things right, not just when, as I so often do, I do things wrong. The lesson I learned is that I am too willing to skip testing something if it looks difficult. And frankly, I m a fanatic about testing, especially customer tests, compared to a lot of folks I know. My fear about how hard it would be was high. The actual effort was quite low, and we now have tests that work better, and a new and simple testing technique for our bag of tricks.

Second, there was that wake-up call when the test didn t run. I had gotten a little careless and walked away from the machine on a red bar, and it took a few minutes to get back up to speed. If I had run the tests before changing anything, it might have been better. I wouldn t have gotten my thoughts all wrapped around getting the menu and then had to reset. But the best thing, I think, is to always stop on a green bar. Lesson learned, again. We ll see how many more times I have to learn it.

Third, we solved this problem in short, almost stupid steps. Now, I m still pretty new to C#, and I was uncertain about getting the menu item and clicking it, but frankly I go in steps almost that small all the time. It works well for me. When I go in bigger steps, my stress level goes up and sometimes I have a lot of debugging to do before I get things working.

Some people get concerned that going in these small steps means that a lot of rework happens. Well, formally speaking, maybe it is rework, but most of what we re doing is adding, not replacing. The total typing isn t much larger than if we did the whole job, and the advantage is that we never have to fit the whole job into our tiny little heads. To me, that s a powerful advantage.

Three lessons this time. sb Push a little when something seems to hard; keep the tests on green; sb build simple scaffolding and then improve it. Enough for this time; see you in the next chapter.

 

Категории