Unit testing - suitable for all development?

Roy Smith roy at panix.com
Sun Mar 7 12:50:21 EST 2004


kylotan at hotmail.com (Kylotan) wrote:
> Today I tried to implement some sort of unit testing into my program
> for the first time, and must admit to being a little disillusioned
> with the process. Mainly, my issue is that in my program, the classes
> are so tightly coupled that testing in isolation is next to
> impossible.

Yup, this is often a problem when you first start unit testing.  Tightly 
coupled classes are difficult to test.

Tightly coupled classes cause lots of problems too.  They make it hard 
to understand how your system works, and they make it hard to change out 
components later.  One of the things that unit testing does is it forces 
you to write classes so they are easily testable, which usually means 
not a lot of inter-class dependencies.

> The main problem stems from the fact that I try to ensure that all my
> objects are created in a working state. This often means passing
> various other objects to the __init__ function.

That's fine.  Let's imagine you've got:

class Foo:
   def __init__ (self, language, timeZone, hairColor):
      self.language = language
      self.timeZone = timeZone
      self.hairColor = hairColor

   def getBreakfastFood (self):
      do a lot of stuff involving language, etc
      return (food)

class Bar:
   def __init__ (self, foo):
      self.foo = foo

On the surface, it looks like you can't test Bar without having a 
working Foo.  But maybe you can provide a stub implementation of Foo 
which does just enough to allow Bar to be tested:

class FooStub:
   def getBreakfastFood (self):
      return "spam"

then you can do in your TestCase class, something like:

def setUp (self):
   foo = FooStub ()
   self.bar = Bar (foo)
  
and you're all set.  This assumes that there's something about Bar's 
behavior which depends on foo having a getBreakfastFood () method which 
actually works.  If the classes were less coupled, Bar might do nothing 
with the foo it was passed other than treat it as opaque data to be 
retrieve by a getFoo() method. In which case, your stub class could be 
even simplier:

class FooStub:
   pass

and you might even be able to get away with no FooStub class at all, but 
simply instantiating your Bar test object with a constant:

def setUp (self):
   self.bar = Bar ("foo")

This is where the dynamic nature of Python really shines.  Something 
like C++ or Java would make you jump through a lot more hoops to make 
sure you instantiated your Bar with a valid Foo.  But the basic 
principle is the same in any OOPL; the more tighly coupled your classes 
are, the more difficult it is to test, maintain, and understand the 
system.

> One way out of this would be to reduce coupling. This would allow me
> to test objects in relative isolation, but it would increase the
> amount of explicit coupling code that I'd have to execute normally.
> This extra code then becomes a potential source of new bugs.

One way or another, your classes need to interact, and the code that 
implements those interactions needs to exist (and thus needs to be 
tested).  The question is, where do you put that code?  Do you bury it 
inside the classes, making it difficult to test both the underlying 
classes and their interactions, or do you factor it out to someplace 
where you can test each piece in isolation?



More information about the Python-list mailing list