Login | Register For Free | Help
Search for: (Advanced)

Mailing List Archive: Python: Python

Art of Unit Testing: Part 2

 

 

Python python RSS feed   Index | Next | Previous | View Threaded


jessefw at loop

Aug 24, 2001, 11:51 AM

Post #1 of 14 (690 views)
Permalink
Art of Unit Testing: Part 2

Dear Python-list-iners,
I have another Testing related question. How are unit tests done
when the units to be tested get lots of information from nested
objects, e.g. (in a method of a class to be tested):
if self.app.cnt_player.battle.kind=='stop':
how would this be tested?
Should you simulate the parts of the app object that are needed?
In that case, you have to update the tests when you use parts of the
app object you did not use before. Should you use a real app
object? Then you are not really doing _unit_ testing, and it would
make running detailed tests very difficult? Is there another solution
that anyone knows about?

Thank you all for your time,
Jesse W


wtanksley at bigfoot

Aug 24, 2001, 1:02 PM

Post #2 of 14 (666 views)
Permalink
Art of Unit Testing: Part 2 [In reply to]

On Fri, 24 Aug 2001 11:51:07 -0700, Jesse F. W wrote:
>Dear Python-list-iners,
> I have another Testing related question. How are unit tests done
>when the units to be tested get lots of information from nested
>objects, e.g. (in a method of a class to be tested):
>if self.app.cnt_player.battle.kind=='stop':
>how would this be tested?

It wouldn't -- it would be deleted and abhorred. That's a violation of
encapsulation and of the law of Demeter (google that if you don't know
what it means).

The problem is that each object should have an interface to do everything
it needs to do. You shouldn't have to reach inside any object to inspect
how it's doing its job. The above code should look like 'if
self.app.anyPlayersInStoppedBattles():' (I'm assuming that's an
appropriate name).

However, your question still remains: how do you unit test objects which
are supposed to delegate operations to other objects? The answer is that
you write the unit test without assuming any delegation. That method you
mentioned should work whether or not delegation is happening; and your
test won't care, because it only tests the method, not the implementation.

>Jesse W

--
-William "Billy" Tanksley


jessefw at loop

Aug 24, 2001, 4:59 PM

Post #3 of 14 (694 views)
Permalink
Art of Unit Testing: Part 2 [In reply to]

William Tanksley wrote:
> On Fri, 24 Aug 2001 11:51:07 -0700, Jesse F. W wrote:
<snip>
> >if self.app.cnt_player.battle.kind=='stop': how would this be tested?
>
> It wouldn't -- it would be deleted and abhorred. That's a violation
> of encapsulation and of the law of Demeter (google that if you don't
> know what it means).

I have looked it up on google. For the lurkers, (and my own
greater knowledge), I will rephrase what I found: The Law of
Demeter means that you should have as few dots(in Python) in your
code as possible. You should make special functions to do
whatever you need done, instead of just doing the thing where you
need it done.(Yes, I know that's a sort of pejoritive way of putting it)
:-)

> The problem is that each object should have an interface to do
> everything it needs to do. You shouldn't have to reach inside any
> object to inspect how it's doing its job. The above code should look
> like 'if self.app.anyPlayersInStoppedBattles():' (I'm assuming that's
> an appropriate name).
>
> However, your question still remains: how do you unit test objects
> which are supposed to delegate operations to other objects? The
> answer is that you write the unit test without assuming any
> delegation. That method you mentioned should work whether or not
> delegation is happening; and your test won't care, because it only
> tests the method, not the implementation.

Now this, I don't understand(yet). What do you mean, "delegate
operations to other objects?" What the example code I included
above does is check wheather the current player in the game is
stopped. I don't see how the method could work if there was no
current player object. Also, please say more about your last
sentence, about testing only the method, not the implementation.

Thank you very much,
Jesse W


johnroth at ameritech

Aug 24, 2001, 10:32 PM

Post #4 of 14 (690 views)
Permalink
Art of Unit Testing: Part 2 [In reply to]

"Jesse F. W" <jessefw [at] loop> wrote in message
news:mailman.998679338.16044.python-list [at] python
> Dear Python-list-iners,
> I have another Testing related question. How are unit tests done
> when the units to be tested get lots of information from nested
> objects, e.g. (in a method of a class to be tested):
> if self.app.cnt_player.battle.kind=='stop':
> how would this be tested?

> Should you simulate the parts of the app object that are needed?
> In that case, you have to update the tests when you use parts of the
> app object you did not use before. Should you use a real app
> object? Then you are not really doing _unit_ testing, and it would
> make running detailed tests very difficult? Is there another solution
> that anyone knows about?

That's part of "Design for Testing." The short answer is that
if the object you are testing needs an infrastructure, you have to supply
it.
It's preferable to supply the "real" infrastructure from the project, but
sometimes it isn't feasible.

If your class needs a player object for it to make sense, then you need
to supply such an object, properly updated. FREX - a project I'm
working on needs a date to start a fairly involved astronomical
calculation. I need to supply a properly initialized date object, or it
won't work.

Designing for test suggests that my date object be fully tested first,
then it can simply be used as a component in later tests. It also
suggests that I keep the dependencies as simple as possible. My date
object, in certain circumstances, needs a place object to determine
the time zone, which in turn needs a time zone change atlas to determine
which time variation applied. For higher level tests, I therefore always
specify GMT, which doesn't need the location information. I'm not,
after all, testing the date object, simply using it.

If you're having trouble, I'd suggest that you look at the design with
the question "how am I going to test this?" in mind. You may want to
refactor some of it.

John Roth

> Thank you all for your time,
> Jesse W
>


juneaftn at orgio

Aug 25, 2001, 6:16 AM

Post #5 of 14 (682 views)
Permalink
Art of Unit Testing: Part 2 [In reply to]

"Jesse F. W" <jessefw [at] loop> wrote in message news:<mailman.998679338.16044.python-list [at] python>...
> Dear Python-list-iners,
> I have another Testing related question. How are unit tests done
> when the units to be tested get lots of information from nested
> objects, e.g. (in a method of a class to be tested):
> if self.app.cnt_player.battle.kind=='stop':
> how would this be tested?

First of all, it really is a Bad Smell. It obviously violates
LoD(which is not so obvious though). Formal definition of LoD goes
something like(for full and exact definition you should read the
original paper, "Assuring good style for object-oriented programs"
IEEE Software, pages 38--48, September 1989):

For an object O of class C and for any method m defined for O, each
receiver of a message within m must be one of the following objects:

1. O itself
2. any objects passed into m
3. any objects O created
4. any composite objects
5. any objects referred to by a global variable

There are some variation versions of LoD, but all of them share the
idea of "Tell Don't Ask." The Pragmatic Programmers have rendered it
so nicely, and I think it's decent enough as today's LoD.
(http://www.pragmaticprogrammer.com/ppllc/papers/1998_05.html)


> Should you simulate the parts of the app object that are needed?
> In that case, you have to update the tests when you use parts of the
> app object you did not use before. Should you use a real app
> object? Then you are not really doing _unit_ testing, and it would
> make running detailed tests very difficult? Is there another solution
> that anyone knows about?

Unit Tests test the intention of units. We don't care about how the
unit process internally, as long as the unit does its work. Moreover,
if the unit tests depend upon the internals of the units, more often
than not, you have to change the unit tests, which could be hazardous,
when refactoring the code.

If the unit was to return a list of customers, you don't want worry
about how it produces the object, whether by walking through data or
by conneting to DB, whatever. And, remember that naming is one of the
most important practice for good codes, and it should reveal the
intention. Therefore, if the method was getCustomersList, we test if
it returns customers list(object), no more no less. By any chance, if
you have refactored the code into several responsibilities, such as
removeDeadCustomers, you test the code if it really removes dead
customers.

Sever the unit that you want to test, and test it as independently as
you can.

If you can't cut the dependency chain easily, you should consider
using mock objects first. (Actually, if you do TestDrivenDesign or
TestFirstProgramming, you wouldn't worry about those -- good
dependency management, and easy to mock up. Testability is given from
the very beginning.)

One of the most famous papers on mock objects was presented at
XPUniverse 2000
(http://www.cs.ualberta.ca/~hoover/cmput401/XP-Notes/xp-conf/papersList.html)
, of which papers has been published as a book "XP Examined." -- worth
a read. Its original URL is, AFAIK,
http://www.sidewize.com/company/mockobjects.pdf

We have a nice Python mock objects module already. It's at
http://groups.yahoo.com/group/extremeprogramming/files/PythonMock.zip

Since the code is short, I include it in this mail. (BTW, the code is
originally written by Dave Kirby, and I added one method hasParam,
which calls unittest modules asserts, so that I can use it with
unittest module easily)

With this module, you can do:

>>> import mock
>>> customers=mock.Mock({'getCustomerList':('Dave','Peter','Laura')})
>>> customers.getCustomerList()
>>> customers.someMethod(1,200,name='Charlie')
>>> print customers.getAllCalls()
[getCustomerList(), someMethod(1, 200, name='Charlie')]
>>> someMethodCall=customers.getNamedCalls('someMethod')
>>> print someMethodCall[0].getParam('name')
Charlie
>>> print someMethodCall[0].getParam(1)
200
>>> someMethodCall[0].hasParam(1,200,name='Charlie')

If the legacy code's unit to test uses a specific external module, and
you want to test the unit before refactoring to improve the dependency
relationships, you can simply substitute the external module with mock
objects.

Say, we have the following legacy code in customers.py:

import customerDB

class Customers:
....
def getCustomersList(self):
allCustomers=customerDB.getAll()
....

We can test it as in the following test code:
import unittest, mock
import sys
from customers import Customers

class TestGetCustomersList:
def testReturnAll(self):
mockCustomerDB=mock.Mock({'getAll':('Charlie','Dave','None')})
sys.modules['customerDB']=mockCustomerDB
customers=Customers(...)
self.assertEqual(('Charlie','Dave'),customers.getCustomersList())

#
# (c) Dave Kirby 2001
# dkirby [at] bigfoot
#
'''
The Mock class emulates any other class for testing purposes.
All method calls are stored for later examination.
The class constructor takes a dictionary of method names and the
values
they return. Methods that are not in the dictionary will return None.
'''
import unittest

class Mock:
def __init__(self, returnValues={} ):
self.mockCalledMethods = {}
self.mockAllCalledMethods = []
self.mockReturnValues = returnValues

def __getattr__( self, name ):
return MockCaller( name, self )

def getAllCalls(self):
'''return a list of MockCall objects,
representing all the methods in the order they were called'''
return self.mockAllCalledMethods

def getNamedCalls(self, methodName ):
'''return a list of MockCall objects,
representing all the calls to the named method in the order
they were called'''
return self.mockCalledMethods.get(methodName, [] )

class MockCall(unittest.TestCase):
def __init__(self, name, params, kwparams ):
self.name = name
self.params = params
self.kwparams = kwparams
def getParam( self, n ):
if type(n) == type(1):
return self.params[n]
elif type(n) == type(''):
return self.kwparams[n]
else:
raise IndexError, 'illegal index type for getParam'
def getName(self):
return self.name
def hasParam(self,*params,**kwparams):
self.assertEqual(tuple(self.params),tuple(params))
self.assertEqual(self.kwparams,kwparams)

#pretty-print the method call
def __str__(self):
s = self.name + "("
sep = ''
for p in self.params:
s = s + sep + repr(p)
sep = ', '
for k,v in self.kwparams.items():
s = s + sep + k+ '='+repr(v)
sep = ', '
s = s + ')'
return s
def __repr__(self):
return self.__str__()

class MockCaller:
def __init__( self, name, mock):
self.name = name
self.mock = mock
def __call__(self, *params, **kwparams ):
thisCall = MockCall( self.name, params, kwparams )
calls = self.mock.mockCalledMethods.get(self.name, [] )
if calls == []:
self.mock.mockCalledMethods[self.name] = calls
calls.append(thisCall)
self.mock.mockAllCalledMethods.append(thisCall)
return self.mock.mockReturnValues.get(self.name)


ngps at post1

Aug 25, 2001, 8:50 AM

Post #6 of 14 (672 views)
Permalink
Art of Unit Testing: Part 2 [In reply to]

According to John Roth <johnroth [at] ameritech>:
> "Jesse F. W" <jessefw [at] loop> wrote in message
> news:mailman.998679338.16044.python-list [at] python
> > Should you simulate the parts of the app object that are needed?

> If your class needs a player object for it to make sense, then you need
> to supply such an object, properly updated.

Not always. You might be able to fake it with "mock objects":

http://mail.python.org/pipermail/python-list/2001-March/035546.html



--
Ng Pheng Siong <ngps [at] post1> * http://www.post1.com/home/ngps

Quidquid latine dictum sit, altum viditur.


wtanksley at bigfoot

Aug 25, 2001, 11:47 AM

Post #7 of 14 (683 views)
Permalink
Art of Unit Testing: Part 2 [In reply to]

On Fri, 24 Aug 2001 16:59:41 -0700, Jesse F. W wrote:
>William Tanksley wrote:
>> On Fri, 24 Aug 2001 11:51:07 -0700, Jesse F. W wrote:

>> >if self.app.cnt_player.battle.kind=='stop': how would this be tested?

>> It wouldn't -- it would be deleted and abhorred. That's a violation
>> of encapsulation and of the law of Demeter (google that if you don't
>> know what it means).

> I have looked it up on google. For the lurkers, (and my own
>greater knowledge), I will rephrase what I found: The Law of
>Demeter means that you should have as few dots(in Python) in your
>code as possible. You should make special functions to do
>whatever you need done, instead of just doing the thing where you
>need it done.(Yes, I know that's a sort of pejoritive way of putting it)
>:-)

That's reasonable, yes. Another way to look at things is that no code
should tell some other object how to do its job. In this case you're
reaching in through 3 objects and expecting to get a specific result --
that REALLY makes writing those three objects hard.

>> The problem is that each object should have an interface to do
>> everything it needs to do. You shouldn't have to reach inside any
>> object to inspect how it's doing its job. The above code should look
>> like 'if self.app.anyPlayersInStoppedBattles():' (I'm assuming that's
>> an appropriate name).

>> However, your question still remains: how do you unit test objects
>> which are supposed to delegate operations to other objects? The
>> answer is that you write the unit test without assuming any
>> delegation. That method you mentioned should work whether or not
>> delegation is happening; and your test won't care, because it only
>> tests the method, not the implementation.

>Now this, I don't understand(yet). What do you mean, "delegate
>operations to other objects?" What the example code I included
>above does is check wheather the current player in the game is
>stopped.

The fact that I couldn't tell what that code did was a good clue that it
was written wrong. You could have changed the names of the objects to
make the test make more sense -- for example,

if self.theGame.current_player.battle.kind=='stop':

But even this doesn't say to me "check whether the current player in the
game is stopped." It actually says, "check whether the game's current
player's battle is of the stopped kind."

>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 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.

Of course, you'll recognise that I'm still violating the law of Demeter.
The reason I'm doing that is that I don't know why each player needs a
"battle". Are you implying that there's a battle going on inside every
player? Perhaps your battles should contain players, rather than your
players containing battles. At any rate, what does it mean to ask a
player's battle whether it is of the stopped kind? Why does having a
stopped battle also stop the player?

>Also, please say more about your last sentence, about testing only the
>method, not the implementation.

An excellent question. I'll answer in the form of a list of rules, with
the first rule being by far the most important (in fact, all the others
follow from it).

Wait. First let me remove a possible unclarity: when I say "method" above
I'm talking about the procedures which form an object.

1. Write your test before you've decided how to code the guts of your
object.
2. When you're testing an object, test ONLY properties of that object,
never properties of contained objects.
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.

Does this make sense?

If you follow these rules, your tests will not only be tests; they will
also be documentation. Furthermore, they will document how to use the
object correctly, not how the object works internally. When you test only
the methods and properties an object exposes, you don't have to worry
about testing the implementation -- and that means that you can change the
implementation if you feel like it. (The last sentance uses the words
"method" and "implementation" in order to make it clear that I'm still
talking about testing the methods, not the implementation.)

> Jesse W

--
-William "Billy" Tanksley


johnroth at ameritech

Aug 26, 2001, 7:43 PM

Post #8 of 14 (665 views)
Permalink
Art of Unit Testing: Part 2 [In reply to]

"Ng Pheng Siong" <ngps [at] madcap> wrote in message
news:9m8hg4$o9c$1 [at] dahlia
> According to John Roth <johnroth [at] ameritech>:
> > "Jesse F. W" <jessefw [at] loop> wrote in message
> > news:mailman.998679338.16044.python-list [at] python
> > > Should you simulate the parts of the app object that are needed?
>
> > If your class needs a player object for it to make sense, then you need
> > to supply such an object, properly updated.
>
> Not always. You might be able to fake it with "mock objects":
>
> http://mail.python.org/pipermail/python-list/2001-March/035546.html

As long as it looks like a duck, walks like a duck, and quacks like a duck
it's a duck. It really doesn't matter whether it's the production class or a
testing simulation thereof, except that using the production version is
likely
to inspire greater confidence in the integrity of the testing effort.

John Roth
>
>
> --
> Ng Pheng Siong <ngps [at] post1> * http://www.post1.com/home/ngps
>
> Quidquid latine dictum sit, altum viditur.


jessw at loop

Aug 26, 2001, 9:26 PM

Post #9 of 14 (688 views)
Permalink
Art of Unit Testing: Part 2 [In reply to]

Thank you very much, Billy. This is very interesting and useful.
After this discussion, I may actually be able to _use_ the wonderful
ideas I have been reading about on various XP websites. :-)
William Tanksley wrote:
> On Fri, 24 Aug 2001 16:59:41 -0700, Jesse F. W wrote:
> >William Tanksley wrote:
> >> On Fri, 24 Aug 2001 11:51:07 -0700, Jesse F. W wrote:
> >> >if self.app.cnt_player.battle.kind=='stop': how would this be
> >> >tested?
>
> >> It wouldn't -- it would be deleted and abhorred. That's a
> >> violation of encapsulation and of the law of Demeter (google that
> >> if you don't know what it means).
<snip definition of Law of Demeter>
> >> The problem is that each object should have an interface to do
> >> everything it needs to do. You shouldn't have to reach inside any
> >> object to inspect how it's doing its job. The above code should
> >> look like 'if self.app.anyPlayersInStoppedBattles():' (I'm assuming
> >> that's an appropriate name).
Ok. This still seems somewhat excessive to me, but I'll let it go for
now.
<another snip>
> The fact that I couldn't tell what that code did was a good clue that
> it was written wrong. You could have changed the names of the objects
> to make the test make more sense -- for example,
>
> if self.theGame.current_player.battle.kind=='stop':
>
> But even this doesn't say to me "check whether the current player in
> the game is stopped." It actually says, "check whether the game's
> current player's battle is of the stopped kind."
Now, although your point that if you could not understand what the
code did, it was probably written wrong has value, and especially if I
have to try twice before I can explain it correctly, I am going to try
and explain it again. :-)
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.)
> >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. 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.
> 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.
> Of course, you'll recognise that I'm still violating the law of
> Demeter. The reason I'm doing that is that I don't know why each
> player needs a "battle". Are you implying that there's a battle going
> on inside every player? Perhaps your battles should contain players,
> rather than your players containing battles. At any rate, what does
> it mean to ask a player's battle whether it is of the stopped kind?
> Why does having a stopped battle also stop the player?
>
> >Also, please say more about your last sentence, about testing only
> >the method, not the implementation.
Thank you for your answer to this question, by the way.
> An excellent question. I'll answer in the form of a list of rules,
> with the first rule being by far the most important (in fact, all the
> others follow from it).
>
> Wait. First let me remove a possible unclarity: when I say "method"
> above I'm talking about the procedures which form an object.
>
> 1. Write your test before you've decided how to code the guts of your
> object.
Ok.
> 2. When you're testing an object, test ONLY properties of
> that object, never properties of contained objects.
Ok; that's the law of Demeter.
> 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?
> Does this make sense?
>
> If you follow these rules, your tests will not only be tests; they
> will also be documentation. Furthermore, they will document how to
> use the object correctly, not how the object works internally. When
> you test only the methods and properties an object exposes, you don't
> have to worry about testing the implementation -- and that means that
> you can change the implementation if you feel like it. (The last
> sentance uses the words "method" and "implementation" in order to make
> it clear that I'm still talking about testing the methods, not the
> implementation.)
>
> > Jesse W
>
> --
> -William "Billy" Tanksley
> --
> http://mail.python.org/mailman/listinfo/python-list
>


jessw at loop

Aug 27, 2001, 12:52 PM

Post #10 of 14 (689 views)
Permalink
Art of Unit Testing: Part 2 [In reply to]

I have now published the code for the game I have been
referring to in this thread on my website:
http://members.loop.com/~jessw/
The file(it is a zip file, sorry) is called miles.zip.
If any of you are interested, please check it out.

Thank you,
Jesse W


wtanksley at bigfoot

Aug 27, 2001, 4:32 PM

Post #11 of 14 (690 views)
Permalink
Art of Unit Testing: Part 2 [In reply to]

On Sun, 26 Aug 2001 21:26:34 -0700, Jesse W wrote:
>William Tanksley wrote:
>> On Fri, 24 Aug 2001 16:59:41 -0700, Jesse F. W wrote:
>> >William Tanksley wrote:
>> >> On Fri, 24 Aug 2001 11:51:07 -0700, Jesse F. W wrote:
>> >> >if self.app.cnt_player.battle.kind=='stop': how would this be
>> >> >tested?

>> >> The problem is that each object should have an interface to do
>> >> everything it needs to do. You shouldn't have to reach inside any
>> >> object to inspect how it's doing its job. The above code should
>> >> look like 'if self.app.anyPlayersInStoppedBattles():' (I'm assuming
>> >> that's an appropriate name).

>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 suggested that name based on a guess. I now know a lot more about your
code; not only could I suggest a better name, I could possibly suggest a
different design which would make sense to both of us.

>Now, although your point that if you could not understand what the
>code did, it was probably written wrong has value, and especially if I
>have to try twice before I can explain it correctly, I am going to try
>and explain it again. :-)

Your explanation was perfect this time. Thank you.

> 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.)

It doesn't look at all sarcastic; I know programmers who write names like
that without wincing. I can tell that you don't like it, and neither do
I. Unfortunately, coming up with a better name requires me to know a
little more of the context. Right now all I know is that this information
is a property of an object named 'app'. App doesn't mean _anything_ to
me, but I'm going to guess that it means "application". I don't know why
an application should be able to know whether a player in a game is
stopped, so the first thing I'd do is rename "app" to "theGame".

Perhaps it makes sense to say "theGame.currentPlayerStopped()". What do
you think? (You answer below, so let's see:)

>> >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.

>> 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.

>> Of course, you'll recognise that I'm still violating the law of
>> Demeter. The reason I'm doing that is that I don't know why each
>> player needs a "battle". Are you implying that there's a battle going
>> on inside every player? Perhaps your battles should contain players,
>> rather than your players containing battles. At any rate, what does
>> it mean to ask a player's battle whether it is of the stopped kind?
>> Why does having a stopped battle also stop the player?

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 :-). Given that, I
think my implementation of isStopped is sufficient, and doesn't violate
the law of demeter.

>> 2. When you're testing an object, test ONLY properties of
>> that object, never properties of contained objects.
>Ok; that's the law of Demeter.

Yes, the law of Demeter does require that.

>> 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.

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.

--
-William "Billy" Tanksley


dwig at advancedmp

Aug 27, 2001, 4:33 PM

Post #12 of 14 (688 views)
Permalink
Art of Unit Testing: Part 2 [In reply to]

Jesse W writes:
> Dear Python-list-iners,
> I have another Testing related question. How are unit tests done
> when the units to be tested get lots of information from nested
> objects, e.g. (in a method of a class to be tested):
> if self.app.cnt_player.battle.kind=='stop':
> how would this be tested?

The (overly) simple answer here is, "don't do that" (the code, not the
testing). In the terms of Fowler's "Refactoring" book, it's a code smell.

For a better understanding of the reasons for avoiding this kind of object
structure navigation, see the "Law of Demeter" section in Brad Appleton's
"Introducing Demeter and its Laws" at
http://www.enteract.com/~bradapp/docs/demeter-intro.html

Also look at the discussion of this law/principle/guideline at
http://www.c2.com/cgi/wiki?LawOfDemeter. About 2/3 down, you'll find
"Demeter and Unit Tests", which addresses your example.

Don't expect your reading to result in a simple "always do it this way"
algorithm. You're getting into some serious object design issues, like
factoring responsibilities among objects, and defining object contracts.

--
Don Dwiggins "Solvitur Ambulando"
Advanced MP Technology
dwig [at] advancedmp


jessefw at loop

Aug 27, 2001, 8:14 PM

Post #13 of 14 (677 views)
Permalink
Art of Unit Testing: Part 2 [In reply to]

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


wtanksley at bigfoot

Sep 4, 2001, 4:02 PM

Post #14 of 14 (683 views)
Permalink
Art of Unit Testing: Part 2 [In reply to]

On Mon, 27 Aug 2001 20:14:05 -0700, Jesse F. W wrote:
>Dear Billy,
> Thank you again; this discussion is really making XP and
>related subjects much clearer to me. Thank you.

My pleasure. For more info, you really should go to
comp.software.extreme-programming. Be sure you've read XP Installed
(available online) first (or at least be reading it), and check out
http://www.xprogramming.com and http://www.extremeprogramming.org/.

The questions you're asking are very good, but I can't answer them without
taking far too much time. I'm going to give you general principles and
hints rather than answers -- especially since by the nature of things, you
have to find the answers for yourself anyhow.

>William Tanksley wrote:
>> On Sun, 26 Aug 2001 21:26:34 -0700, Jesse W wrote:

>> >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)

Redesign. Let's look below for detail; for now, just note that in a good
design, every object has a clear purpose of its own, and all of that
object's methods and members serve that one purpose. This is called
/cohesion/. A good design has very high cohesion.

Another word you'll hear in design is /coupling/. Coupling is what
happens when two modules or objects depend on each other's internal
details; in other words, a single object isn't enough to serve a single
purpose.

Your current design has bad cohesion, because the app object serves
multiple poorly-defined purposes; it also has bad coupling, because the
player object needs to know the internal details of the objects it
contains.

>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.

It's interesting that your Player class uses the app object; in other
words, the entire game is contained in the player. Does this fit your
mental model of how a game's played?

My analysis is a little different:

A game contains a number of Players, one Draw deck, and one Discard deck.
There's one Current player, who is offered the chance to play; the game
passes to him some PlayerViews and the game's two decks. A PlayerView can
report total mileage, invulnerabilities, speed limits, and battle status.

There's some elaboration to be done here; but it matters little. You see
the basic idea of how I arrange the game. Note that Player and PlayerView
are two seperate objects! This is because the task of showing people your
cards is different from the task of playing the game.

>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.)

This seems intuitively global to the game at hand. If your application
serves no purpose but to play MB, then this is sufficient. If not, you
may have to do some more design -- some ideas occur to me, but time
prevents.

> * tell the graphics object to do various things

Sounds like a good job for a Graphics object.

> * ask the game object for various things

Sounds like a good job for the Game object.

>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.

The Players in my design are part of the Game. Where, do you ask, can the
Display object go? The best place, IMO, for it is in the HumanPlayer
object, which is one of the Players. The Display object shows only what
the Game allows every Player to see.

>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?

Does my little analysis provide the beginning of an answer to you?

>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.

Yes! Exactly. Now you're discovering a new type of test-first
programming, called "test-first by intention." When you write test-first
by intention, not only do you write your tests before you write your code;
you also design your tests in the way that seems easiest, almost
regardless of how much it might take to code.

For examples of this (and a rationale), please read XP Installed.

> Thank you very very much,

My pleasure! Teaching is the best way to learn.

> Jesse W

--
-William "Billy" Tanksley

Python python RSS feed   Index | Next | Previous | View Threaded
 
 


Interested in having your list archived? Contact Gossamer Threads
 
  Web Applications & Managed Hosting Powered by Gossamer Threads Inc.