Unit testing - suitable for all development?

Discussion in 'Python' started by Kylotan, Mar 7, 2004.

  1. Kylotan

    Kylotan Guest

    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.

    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. In turn, these other
    objects rely on others, and on general initialisation procedures. The
    end result is that there are very few objects I can truly test in
    isolation; I have to initialise at least 80% of the system before I
    can even create most of my objects. This ends up meaning that unit
    testing isn't really testing a single unit at all, and in fact isn't
    much more precise than liberally using asserts in the normal code.

    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.

    Has anybody else come to similar conclusions?

    --
    Ben Sizer
     
    Kylotan, Mar 7, 2004
    #1
    1. Advertising

  2. Kylotan

    John Roth Guest

    "Kylotan" <> wrote in message
    news:...
    > 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.
    >
    > 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. In turn, these other
    > objects rely on others, and on general initialisation procedures. The
    > end result is that there are very few objects I can truly test in
    > isolation; I have to initialise at least 80% of the system before I
    > can even create most of my objects. This ends up meaning that unit
    > testing isn't really testing a single unit at all, and in fact isn't
    > much more precise than liberally using asserts in the normal code.
    >
    > 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.
    >
    > Has anybody else come to similar conclusions?


    Coupling is the enemy.

    The XP people have a lot of experiance with reducing
    coupling. Writing tests first, and insisting that literally
    hundreds of unit tests run in a few seconds teaches you
    how to write code with a minimum of coupling. Some of
    those lessons aren't particularly easy.

    I'm very much a fan of creating objects in a state so that
    the object invariants pass. I've got two suggestions.

    The first is to see if you can redesign using a layered
    architecture. That may force you to reduce the number
    of objects that any given object depends on.

    The second is to investigate mock objects, or other
    methods of stubbing out dependencies. While you do
    need to run the entire system as a unit, there is no
    reason you need to run the real production objects
    for all tests.

    John Roth
    >
    > --
    > Ben Sizer
     
    John Roth, Mar 7, 2004
    #2
    1. Advertising

  3. Kylotan

    Roy Smith Guest

    (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?
     
    Roy Smith, Mar 7, 2004
    #3
  4. Kylotan

    Kylotan Guest

    "John Roth" <> wrote in message news:<>...

    > Coupling is the enemy.
    >
    > The XP people have a lot of experiance with reducing
    > coupling. Writing tests first, and insisting that literally
    > hundreds of unit tests run in a few seconds teaches you
    > how to write code with a minimum of coupling. Some of
    > those lessons aren't particularly easy.


    Yeah, that's what I'm guessing. I'm at the stage where I have to
    wonder whether forcing myself to make the paradigm shift will be worth
    the effort or not.

    > The first is to see if you can redesign using a layered
    > architecture. That may force you to reduce the number
    > of objects that any given object depends on.


    What do you mean by layered in this context? Are you talking things
    like the Bridge design pattern?

    > The second is to investigate mock objects, or other
    > methods of stubbing out dependencies. While you do
    > need to run the entire system as a unit, there is no
    > reason you need to run the real production objects
    > for all tests.


    That seems like a nice idea, but I've been developing this system as
    an evolving prototype. I add new objects and classes as I discover I
    need them. Therefore there's almost no functionality there at all that
    isn't needed for the system as it stands.

    Here's an example of my current system: the Creature class requires a
    Race class and a World class to be instantiated, as the Creature's
    __init__ function relies on calling methods of these objects (not just
    on their presence). Sure, technically I could create dummy objects to
    pass in that have do-nothing functions, but then I break most of
    Creature's accessors, which are implemented in terms of delegating to
    the other objects. In turn this would make the tests meaningless.

    I guess what I'm trying to say is that I create systems that arise
    from the grouping of classes, and that taking the class out of context
    makes it useless. I don't have many things that are low-level enough
    to make sense when tested in isolation. (This contrasts heavily to my
    development in C++, for example.) And the classes have very little
    internal behaviour; it's all done in terms of other classes.

    None of this makes testing any harder or less useful in catching bugs;
    it just means that it ends up being an almost straight duplication of
    my normal code, with the input handling stripped out! It hardly seems
    worth the effort.

    --
    Ben Sizer
     
    Kylotan, Mar 7, 2004
    #4
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. John Maclean
    Replies:
    1
    Views:
    350
    Martin P. Hellwig
    Apr 13, 2010
  2. Ulrich Eckhardt

    unit-profiling, similar to unit-testing

    Ulrich Eckhardt, Nov 16, 2011, in forum: Python
    Replies:
    6
    Views:
    341
    Roy Smith
    Nov 18, 2011
  3. Bill Mosteller
    Replies:
    0
    Views:
    234
    Bill Mosteller
    Oct 22, 2009
  4. Avi
    Replies:
    0
    Views:
    500
  5. Avi
    Replies:
    0
    Views:
    469
Loading...

Share This Page