Art of Unit Testing: Part 2

Jesse F. W jessefw at loop.com
Mon Aug 27 23:14:05 EDT 2001


Dear Billy,
	Thank you again; this discussion is really making XP and 
related subjects much clearer to me.  Thank you.
William Tanksley wrote:
> On Sun, 26 Aug 2001 21:26:34 -0700, Jesse W wrote:
> >Ok.  This still seems somewhat excessive to me, but I'll let it go
> >for now.
> 
> If it looks excessive to you, it's definitely wrong for what you're
> doing. You shouldn't violate the law of Demeter, but you can find a
> solution which doesn't appear excessive to you and which doesn't
> violate the law. One of the main tenets of extreme programming is that
> programming should be fun; if the design of your program is making you
> uncomfortable it's a bad design.
I like that view! :-) 
<snip>

> >	The game I am making is a card game representing a auto 
> >race.  The battle part of the above line refers to the top card in a
> >pile of cards called the battle pile(hence the name).  All cards have
> >a attribute called kind which represents(and I know this is
> >confusing, and should be refactored) the general type of the card. In
> >your terms, the method should be called
> >"CurrentPlayerHasAStopTypeCardOnTopOfTheirBattlePile".  (Ooh, that
> >looks sort of like I am being sarcastic.  Since this is email, I will
> > specifically say, I do not mean to be.)
> 
<snip> 
> >> >I don't see how the method could work if there was no 
> >> >current player object.
> 
> >> The existance of a 'currentPlayer' wouldn't matter to this object
> >> if theGame had a method to answer this important question.  In
> >> order for me to write this code, though, I have to know what it
> >> means to theGame when the current player is stopped.
> >I don't think it would mean anything to the game if the current
> >player was stopped.  The current player could play different cards,
> >but I don't think the game would care.
> 
> Okay.  If the game doesn't care about whether the current player is
> stopped, then nothing above the level of the game should ever care.
> (Since the game is the only thing that knows about the current
> player.)
> 
> >By the way, I use the self.app object mainly as a central storehouse
> >of links to the various subparts of the total program.  The app
> >object does not really know or do much.
> 
> Ah.  I would consider the app object unworthy of existance, then. 
> Every object should have a meaning of its own.  However, if you
> absolutely cannot find a way to give it a meaning, you must violate
> the law of demeter every time you use it.
How do I connect the various parts of my program, then? (I will say 
more about this below)
> >> I can rewite some code right now, though:
> >> class Player:
> >>   def isStopped():
> >>     return self.battle.kind == 'stop'
> >> Now we can reasonably talk about a player being stopped -- players
> >> can tell you whether or not they're stopped.
> 
> >I was going to write here, "I really don't understand what this
> >changes", but just as I was writing it, I understood.  It adds a
> >level of abstraction, allowing the name battle to be changed, and
> >even the name kind or its value, could be set to something else.
> 
> Yes, that's a good insight.  It also allows the code to look like our
> speech.  You mentioned to me that the current player was stopped; to
> me, that indicates that you picture a player as being able to be
> stopped. From this it's natural to give players the ability to tell
> whether they're stopped.
Ok; that makes sense.
<snip>

> I understand now that every player has a number of decks, and one of
> them is the battle deck.  Yes, I've played this game before :-). 
Glad you have played the game.  By the way, the code as it is now 
is available on my website if you want to look it over.  See my last 
message in this thread for the address.
> Given that, I think my implementation of isStopped is sufficient, and
> doesn't violate the law of demeter.
Ok.
<snip>
> 
> >> 3.  Test ALL of the properties of the object which will be used by
> >> any other object. If a property hasn't been tested, don't ever use
> >> it; if you're about to use a property in a manner which hasn't been
> >> tested, write a test for that property and add that test to the
> >> object's unit tests.
> 
> >Ok; but how to you "test the properties" without testing the
> >implemented?
> 
> Let's look at one of the properties of a Player in your game.  I just
> decided that "isStopped" would make a good property.  Now, in order to
> write a test I have to create a player, put him into a non-stopped
> mode, check that isStopped isn't true, and then put him into a stopped
> mode. Probably the best way to do this is to force the Player to play
> a Go, then play a Stop onto his battle stack (typical in Mille
> Bournes), then have him play another Go.
Yes, this is making a lot of sense.
> Note that I'm speaking in terms of the Player object; it sees the
> world in terms of a complete set of game rules.  When I play a Stop
> against him, the Player *knows* that it goes on his battle deck;
> nothing else needs to know about the existance of the battle deck.  In
> fact, you may not actually use a real deck (that is, a complete
> history of all cards); you might simply use a 'stopped' state and a
> most-recently-played card.  Oh, and a speed limit.  The point is that
> only the Player needs to know how he's keeping track of those details.
Right.  Actually, the battle variable does just represent the top card.

	Now, thinking about your suggestions about the existence of the 
app(lication) object, I searched through my Player class for uses of 
the app object. I found I mainly used the app object to:
	* access basic data(e.g. the number of cards in a hand, the 
number of miles necessary to win, etc.)
	* tell the graphics object to do various things
	* ask the game object for various things
I had to use the app object for this because it included references to 
the player objects, the game object and the graphics object.  
Without the app object, I don't know how I would do these things.
	There were also some changes I thought I should make after 
learning about the LofD, etc.  I wanted to change the code for 
drawing a card to use a method of the game, instead of just 
accessing the deck list, and I wanted to have another game method 
to answer the question, whoIsShutout used when calculating scores.
	How would I do the above tasks without a app object?  I just now 
realized another reason for all the methods.  They allow fake testing 
methods to be simpler and easier to program, because they only 
need to fake answers to questions, or sometimes provide objects, 
not actually _do_ anything. Neat.

	Thank you very very much,
		Jesse W




More information about the Python-list mailing list