473,387 Members | 1,540 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,387 software developers and data experts.

help: Unit test fixture returning the same object

Hi, I have a problem with unittest.TestCase that I could really use
some help with.

I have a class File in module File. The important thing about File for
this discussion is that it's simple - no pool of objects are involved,
and subsequent invocations of File.File('filename') should return
distinct objects (and indeed, they do on the command line). Also,
__repr__ prints out the value of id(self) for File, so I can tell
what's going on here..

I also have a suite of tests that test File in testFile.py
Within, I have a TestCase subclass that looks like this:

class FileReadingTestCase(unittest.TestCase):
def setUp(self):
self.testfilename = "filename"
self.testfile = File.File(self.testfilename)
print 'setup:', self.testfile

def tearDown(self):
print 'teardown:', self.testfile
self.testfile.close()
self.testfile = None
print 'teardown:', self.testfile

.... followed by a bunch of tests that use self.testfile.

The problem is that in each test case, setUp and tearDown are called
as expected according to the print statements, but they don't seem to
have any effect after the first invocation. self.testfile is always
the same instance as reported by id().

There are two strange things going on here:
1 - in tearDown(), self.testfile is indeed being set to None, but in
the subsequent invocation of setUp(), self.testfile is pointing right
back to the same object.

2 - Of course, you can't print the value of self.testfile in setUp()
before it's assigned, that works as expected, but when calling the
constructor for File, you get the same object with the same id.

This doesn't happen in any other context, so I think there is
something about the unittest framework (or just Python) that I don't
understand. Does anyone have any insight?

my python is 2.3, as distributed with Mac OS X 10.3

Thanks,
-mike
Jul 18 '05 #1
10 1795
In article <98**************************@posting.google.com >,
mi***************@mac.com (Michael McCracken) wrote:
The problem is that in each test case, setUp and tearDown are called
as expected according to the print statements, but they don't seem to
have any effect after the first invocation. self.testfile is always
the same instance as reported by id().


Are you really sure they're the same object? id() just returns the
address of the object in memory. It's possible for two objects to have
the same id as long as their lifetimes never overlap. If you repeatedly
create and destroy the same type of objects in the same context (as
happens in a unittest environment), it's reasonably likely that objects
will reuse the same memory locations.

Other than the oddity of id() returning the same value in different
tests, is something unexpected happening?
Jul 18 '05 #2
Michael McCracken wrote:
The problem is that in each test case, setUp and tearDown are called
as expected according to the print statements, but they don't seem to
have any effect after the first invocation. self.testfile is always
the same instance as reported by id().


What Roy said... but to prove it to yourself, if you need to,
just implement a little class-variable-based counter in the
File __init__ method to show that you are getting different
objects each time. (That is, increment a global counter
each time you go through __init__, and store the current
count in the instance, to be retrieved and shown when you
are printing the ids() for the objects.)

-Peter
Jul 18 '05 #3
Peter Hansen <pe***@engcorp.com> wrote in message news:<nK********************@powergate.ca>...
Michael McCracken wrote:
The problem is that in each test case, setUp and tearDown are called
as expected according to the print statements, but they don't seem to
have any effect after the first invocation. self.testfile is always
the same instance as reported by id().


What Roy said... but to prove it to yourself, if you need to,
just implement a little class-variable-based counter in the
File __init__ method to show that you are getting different
objects each time. (That is, increment a global counter
each time you go through __init__, and store the current
count in the instance, to be retrieved and shown when you
are printing the ids() for the objects.)

-Peter


Thanks for the responses!
I think I'm getting pretty close to the solution, but I'd appreciate
some more help.

So, I'm still surprised that id() would point to the same object every
time, but I'm willing to believe it. However, I don't think that's the
only thing that's going on - the reason I noticed this in the first
place is that the setUp method was opening a file and populating some
lists in my File object, and those lists were accumulating objects
between test methods.

The first test sees the File as it expects, while the second one sees
a File with list member variables that have twice as many of each
object it reads in, and so forth. That's why I was willing to believe
that the same id() wasn't a fluke.

I did try the global count idea, and I did get an increasing count,
despite the same id oddity. So new instances seem to be getting
created, with the same data structures. I don't think I'm accidentally
using class variables, and that is the only thing I can think of. Any
more tips?

Thanks,
-mike
Jul 18 '05 #4
mi***************@mac.com (Michael McCracken) wrote:
So, I'm still surprised that id() would point to the same object every
time, but I'm willing to believe it. However, I don't think that's the
only thing that's going on - the reason I noticed this in the first
place is that the setUp method was opening a file and populating some
lists in my File object, and those lists were accumulating objects
between test methods.
My first guess would be that you're using class variables instead of
instance variables, but you say:
I don't think I'm accidentally using class variables


so I'm stumped. Can you post your code?
Jul 18 '05 #5
Michael McCracken wrote:
I did try the global count idea, and I did get an increasing count,
despite the same id oddity. So new instances seem to be getting
created, with the same data structures. I don't think I'm accidentally
using class variables, and that is the only thing I can think of. Any
more tips?


1 Make a minimal script showing the offensive behaviour
2 Post it on c.l.py

Incidentally, step 2 is often unnecessary, because the error (if any) is
found in 1 :-)

More concrete, you could inhibit garbage collection of File instances by
keeping old files around, e. g. in a global list. If ids are still the same
you are definitely seeing the effect of a coding error.

Peter
Jul 18 '05 #6
Michael McCracken wrote:
So, I'm still surprised that id() would point to the same object every
time, but I'm willing to believe it.


Don't think of it using those words. Think of it as "the new
object is being created at the same address as the old object
was at, so it's likely the first object created since the old
one was destroyed", or something like that. At least that way
it's much easier to believe, much less of a coincidence, and
actually something that a number of people have encountered
over the years, though often with similar expressions of surprise
or disbelief. :-)

-Peter
Jul 18 '05 #7
Roy Smith <ro*@panix.com> writes:
mi***************@mac.com (Michael McCracken) wrote:
So, I'm still surprised that id() would point to the same object every
time, but I'm willing to believe it. However, I don't think that's the
only thing that's going on - the reason I noticed this in the first
place is that the setUp method was opening a file and populating some
lists in my File object, and those lists were accumulating objects
between test methods.


My first guess would be that you're using class variables instead of
instance variables, but you say:
I don't think I'm accidentally using class variables


so I'm stumped. Can you post your code?


I managed to fix my problem, but I don't understand why the fix works
yet, so I'm not quite satisfied.

I am trying to narrow it down so I can post more informatively.

So far my attempts at making a minimal example don't exhibit the same
problem. While I'm trying to get the smallest example with the
problem, I will describe what happened and how I 'fixed' it.

The File id() was indeed a red herring.

The problem was that in __init__, File reads a bunch of stuff (in XML
using libxml2) and populates some internal lists. The one that had the
problem was a list of Modules, which has a list of Procedures which
has a list of Blocks. Each Block has predecessors and successors.

Block looks like this:

class Block:
def __init__(self, name, preds = [], succs = []):
self.name = name

self.predecessors = preds
self.successors = succs
self.metrics = []

for pred in self.predecessors:
pred.addSuccessor(self)
for succ in self.successors:
succ.addPredecessor(self)

# add* just append to the lists, avoiding cycles.

def __repr__(self):
s = "{%d -> %s (0x%d) -> %d (%d)}" %\
(len(self.predecessors), self.name,\
id(self), len(self.successors),
len(self.metrics))
return s

In File, Blocks are created in a method that gets called as we're
parsing out the Procedures and Modules. I make a new Block with empty
preds and succs initially, then link them up after they're all
created:

in File.py:

def blockForXMLNode(self, n):
name = safeGetAttribute(n, "label", xlinkNSURI)
# this is called during the first pass, can't link up yet
preds = []
succs = []
block = Block.Block(name)# preds, succs)
print 'DEBUG: Making new block: ', block

The problem: if I run it as is, I get this output:
(During the first testCase:)
DEBUG: Making new block: {0 -> no_exit.1 (0x2714520) -> 0 (0)}
DEBUG: Making new block: {0 -> loopexit.1 (0x2714840) -> 0 (0)}
DEBUG: Making new block: {0 -> loopexit.0 (0x2715000) -> 0 (0)}
(During the next testCase:)
DEBUG: Making new block: {3 -> no_exit.1 (0x2735080) -> 5 (0)}
DEBUG: Making new block: {4 -> loopexit.1 (0x2735400) -> 6 (0)}
DEBUG: Making new block: {5 -> loopexit.0 (0x2735560) -> 7 (0)}
(and so on...)

So it does look like a class variable vs. instance variable problem,
but I can't say why. The fix is to uncomment the "# preds, succs)" and
pass in those empty lists to the Block constructor. That gives the
expected results, but I can't explain the difference.

-mike
--
Jul 18 '05 #8
Michael McCracken wrote:
I managed to fix my problem, but I don't understand why the fix works
yet, so I'm not quite satisfied.
[...]
class Block:
def __init__(self, name, preds = [], succs = []):


Heureka! Providing mutables as default values is a well-known pitfall.

http://www.python.org/doc/faq/genera...etween-objects

The default values are evaluated only once, so you end up sharing
predecessors and successors between all Block instances that are created
without explicitly passing preds and succs. A minimal example:
def f(a, alist=[]): .... alist.append(a)
.... print alist
.... f(1) [1] f(2) [1, 2] f(3) [1, 2, 3]

The standard workaround is to initialize the would-be list with None like
followed by a test in the function body:
def f(a, alist=None): .... if alist is None: alist = []
.... alist.append(a)
.... print alist
.... f(1) [1] f(2) [2] alist = []
f(1, alist) [1] f(2, alist)

[1, 2]

Peter

Jul 18 '05 #9
In article <ce*************@news.t-online.com>,
Peter Otten <__*******@web.de> wrote:
Michael McCracken wrote:
I managed to fix my problem, but I don't understand why the fix works
yet, so I'm not quite satisfied.


[...]
class Block:
def __init__(self, name, preds = [], succs = []):


Heureka! Providing mutables as default values is a well-known pitfall.

http://www.python.org/doc/faq/genera...s-shared-betwe
en-objects


Oh, excellent - thanks for the pointer. Time to read the rest of the
FAQ. :)

-mike
Jul 18 '05 #10

"Peter Hansen" <pe***@engcorp.com> wrote in message
news:ov********************@powergate.ca...
Michael McCracken wrote:
So, I'm still surprised that id() would point to the same object every
time, but I'm willing to believe it.


Don't think of it using those words. Think of it as "the new
object is being created at the same address as the old object
was at, so it's likely the first object created since the old
one was destroyed", or something like that. At least that way
it's much easier to believe, much less of a coincidence, and
actually something that a number of people have encountered
over the years, though often with similar expressions of surprise
or disbelief. :-)


Yes, yes. When an object is destroyed, its id is free for reuse, whatever
the implementation. For an unambiguous example:
id([1]), id([2])

(9692456, 9692456)

Terry J. Reedy

Jul 18 '05 #11

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

6
by: Tom Verbeure | last post by:
Hello All, so I'm now convinced that unit testing is really the way to go and I want to add it to an existing project. The first thing that I find out, is that, well, it's hard work. Harder than...
38
by: Christoph Zwerschke | last post by:
In August 2001, there was a thread about the "Art of Unit Testing": http://groups.google.com/group/comp.lang.python/browse_frm/thread/aa2bd17e7f995d05/71a29faf0a0485d5 Paul Moore asked the...
5
by: Sumit | last post by:
I have scinario like I have to Create resource(in __init__()) Before Running a set of testcases and then In Testcases resources are going to used and then It will cleared off after Running the...
6
by: Ben Finney | last post by:
Howdy all, Summary: I'm looking for idioms in unit tests for factoring out repetitive iteration over test data. I explain my current practice, and why it's unsatisfactory. When following...
15
by: Enrique | last post by:
Question I am posting this question again (3rd time) because some issues with my no spam alias. Here it is the question: I have not been able to run unit tests for a VSTO (2005) project. I...
72
by: Jacob | last post by:
I have compiled a set og unit testing recommendations based on my own experience on the concept. Feedback and suggestions for improvements are appreciated: ...
2
by: shuisheng | last post by:
Dear All, I am using visual studipo 2005 (standard version which do not provide unit test tool) to develop some c++ code. I want to do unit test while I am coding. Anybody can suggest me an...
176
by: nw | last post by:
Hi, I previously asked for suggestions on teaching testing in C++. Based on some of the replies I received I decided that best way to proceed would be to teach the students how they might write...
27
by: brad | last post by:
Does anyone else feel that unittesting is too much work? Not in general, just the official unittest module for small to medium sized projects? It seems easier to write some quick methods that are...
0
by: taylorcarr | last post by:
A Canon printer is a smart device known for being advanced, efficient, and reliable. It is designed for home, office, and hybrid workspace use and can also be used for a variety of purposes. However,...
0
by: aa123db | last post by:
Variable and constants Use var or let for variables and const fror constants. Var foo ='bar'; Let foo ='bar';const baz ='bar'; Functions function $name$ ($parameters$) { } ...
0
by: ryjfgjl | last post by:
If we have dozens or hundreds of excel to import into the database, if we use the excel import function provided by database editors such as navicat, it will be extremely tedious and time-consuming...
0
by: emmanuelkatto | last post by:
Hi All, I am Emmanuel katto from Uganda. I want to ask what challenges you've faced while migrating a website to cloud. Please let me know. Thanks! Emmanuel
1
by: nemocccc | last post by:
hello, everyone, I want to develop a software for my android phone for daily needs, any suggestions?
1
by: Sonnysonu | last post by:
This is the data of csv file 1 2 3 1 2 3 1 2 3 1 2 3 2 3 2 3 3 the lengths should be different i have to store the data by column-wise with in the specific length. suppose the i have to...
0
by: Hystou | last post by:
There are some requirements for setting up RAID: 1. The motherboard and BIOS support RAID configuration. 2. The motherboard has 2 or more available SATA protocol SSD/HDD slots (including MSATA, M.2...
0
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However,...
0
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.