Wednesday, April 04, 2007

Gaming

Tie together
 So, last time I starting with affecting the Character directly, then proceeded to affecting the Character through an Effect. Now I'm going to make the effect a result of an ability (success or failure). I need a story help me figure out the abilities.

Story
 Actually, I've got the story I need. Reading a book (useAbility("ReadBook")) makes people smarter (IncreaseStatistic("Intelligence", 1)). Homer can't read, so this doesn't affect him. Lisa can't get any smarter, so it doesn't affect her, either. I need a new character - someone averge. I'll call him Average Joe.

Add Joe
 Adding Joe to setup. Oops - I accidentally deleted a line that Lisa needed. Fortunately, I was able to find it immediately, as all of my Lisa-related tests failed.

Gaming
 There's a saying that if you want to improve how you play a game, you should keep track of your mistakes (say, by incrementing on a 20-sided die) each time you realize you made a mistake. If I kept track of my little mistakes, I might be able to prevent them. But, more importantly, I would be a little less cavalier about testing. These tests help to instill humility (:

Monday, April 02, 2007

Layers of Indirection

Real Effect
 Ok, time to create my first non-trivial effect. I want to change the statistic on one of the characters. First, I'll write a few tests for boundary conditions.

testMakingLisaSmarterHasNoEffect
testMakingHomerDumberHasNoEffect

testMakingLisaDumber
testMakingHomerSmarter

Can't Make Lisa Smarter
 This fails because the method IncreaseStatistic doesn't exist yet. Writing an empty version now. Great, it passes.
 
Can't Make Homer Dumber
 This test should run as soon as I create it. Interestingly enough, I make a few mistakes in creating the test, so the result isn't what I expect, but they are easily fixed (when detected at this point).
 Context switching is difficult. Catching errors early means that you don't have to context switch. With these tests, I'm catching bugs when it's cheapest to fix them.

Making Lisa Dumber
 Creating failing test first. Now making it pass.

Whoops
 Interestingly, I decide to make this test AND the other tests that I have in mind pass. I bit off more than I could chew, however, and now I have to debug. Which I do.
 Oh, now I've found another problem - the tests are not independant. That is, if I make Lisa dumber in one test, she stays a little dumber in the next test. This is a big problem, which I thought I had solved by passing a copy of the array of parameters (not the array itself). Let me try a few simple things to fix this. First, moving SetupTest and TearDownTest into RunGenericTest.
 Ok, I've fixed that, but now I need to remove a parameter from RunGenericTests, which has been copied many times. I think I should take advantage of this opporunity to reduce some copy/paste smell.
 Ok, I've refactored a bit, so now I can remove the parameter from one call of RunGenericTest (instead of many). Excellent. Simple change. Now to clean up a bit, and not create the parameters array (or call SetupTest/TearDownTest) in the TestAll main function.
 Easily accomplished.

testMakingHomerSmarter
 Ok, one more test. I'm pretty sure that it will pass now, but this will help me make sure that it always works.
 Quickly and easily done.

Indirection
 These tests all work because they affect the character directly. My next tests will use an Effect object (with a new type) to change (or not change) the character's intelligence. That is, replace
 OLD CODE
Call objLisa.IncreaseStatistic(lngIntelligenceID, 1)

 with NEW CODE
Set objEffect = new CEffect
Call objEffect.SetAsIncreaseStatisticEffect(lngIntelligenceID, 1)
Call objEffect.ApplyToCharacter(objLisa)
Set objEffect = Nothing

 I see four more tests:

testIndirectlyMakingLisaSmarterHasNoEffect
 Written, fails because I haven't written the supporting methods yet. I'll write them now as empty. Passed. I'm done with this test.

testIndirectlyMakingHomerDumberHasNoEffect
 Written, should have passed first time, but didn't (I didn't update the function name. Should have passed second time, but didn't (I neglected to declare a variable). Interesting, as running the tests felt like a waste of time (see Devil) because I logically knew that the test would pass. I didn't account for my tremendous ability to make simple mistakes.

testIndirectlyMakingLisaDumber
 This one should fail and make me do some work. Writing failing version now (simply copy and do the replace listed above). Failing, now to make it work.
 Not too bad, just about 10 lines of code. Let's see if it works. Excellent! It does. And I know (from my tests) that it also doesn't do anything "impossible" such as making Homer dumber or Lisa smarter.

testIndirectlyMakingHomerSmarter
 This should be an easy one. Whoo-hoo! It works, AND I've reached a significant milestone - more than 25 tests, so I can see the "paging" for my results summary works. An unexpected bonus.

Next Step?
 Next step is to add another layer of indirection. Instead of creating the Effect directly, I'll make the effect be returned by a successful UseAbility.