For Programmers: Free Programming Magazines  


Home > Archive > Extreme Programming > January 2005 > What is a UNIT?









You are viewing an archived Text-only version of the thread. To view this thread in it's original format and/or if you want to reply to this thread please [click here]

 

Author What is a UNIT?
Gilligan

2005-01-18, 8:59 pm

How do you define a Unit of code?

I know we are supposed to write Unit tests for everything, but what is
a unit?

Everything in the calling hierarchy of code is not a leaf node, but I
have seen some unit tests that are "complete" at the leaf node and
imcomplete everywhere else.

Leaf node code doesn't call any other method of your system. (Clearly
it calls methods)

Example leaf node method:

int Add(int a, int b)
{
return a+b;
}


When a piece of code (which may or may not be a unit depending upon
your definition) calls other methods, creates other objects, etc., the
unit tests for these more complex classes only tested the getters and
setters of simple fields (methods with leaf node behavior) and did not
test the methods that called other methods or created objects and
passed messages to those objects. Why? Because the amount of work to
stub out these called objects was considered expensive.

Do you unit test every method? EVERY ONE????
What do you do? How do you do it?

Thanks,
Geoff

John Roth

2005-01-19, 3:57 am

"Gilligan" <geoffrey.slinker@gmail.com> wrote in message
news:1106078113.354903.289960@f14g2000cwb.googlegroups.com...
> How do you define a Unit of code?
>
> I know we are supposed to write Unit tests for everything, but what is
> a unit?
>
> Everything in the calling hierarchy of code is not a leaf node, but I
> have seen some unit tests that are "complete" at the leaf node and
> imcomplete everywhere else.
>
> Leaf node code doesn't call any other method of your system. (Clearly
> it calls methods)
>
> Example leaf node method:
>
> int Add(int a, int b)
> {
> return a+b;
> }
>
>
> When a piece of code (which may or may not be a unit depending upon
> your definition) calls other methods, creates other objects, etc., the
> unit tests for these more complex classes only tested the getters and
> setters of simple fields (methods with leaf node behavior) and did not
> test the methods that called other methods or created objects and
> passed messages to those objects. Why? Because the amount of work to
> stub out these called objects was considered expensive.
>
> Do you unit test every method? EVERY ONE????
> What do you do? How do you do it?
>
> Thanks,
> Geoff


There are really several parts to this answer. One is
that the word "test" in test driven development is a
bit of a misnomer. Sure it's a test, and we use the
collected suite of tests both as a regression test suite
and as a guide to the design.

However, it's much more a concrete expression of
the design than it is a set of tests of an existing
artifact. The suite of tests you get out of TDD is not
the same tests you would get out of doing classical
testing on the same program text.

The next piece has to do with coupling. Testing
code with low coupling is easy. Testing code with
high coupling is hard to impossible. When you do
TDD, you learn how to design code with low
coupling.

The last piece has to do with substitutability.
It's impossible to have code with no coupling;
the code wouldn't be able to do anything interesting.
So you have to be able to substitute testing objects
for the real production objects in order to isolate
the class (or small cluster of classes) you are
focusing your attention on at any given time.

The issue is that you can't let the class manage
its own dependencies, for example by calling
the new method on a specific class to get an
instance of that class. You need some form of
dependency injection so that the tests can
inject the proper class (or instance) into it
at run time.

There are a number of ways of doing
dependency injection, ranging from passing
them as parameters to using snazzy state
of the art IOC (Inversion of Control)
containers.

John Roth


>


Phlip

2005-01-19, 3:57 am

Gilligan wrote:

> How do you define a Unit of code?
>
> I know we are supposed to write Unit tests for everything, but what is
> a unit?


This is why folks have switched to "Programmer Test" or "Developer Test".

Pro testers do unit tests. TDD does Developer Tests.

The failure of a unit test implicates one unit. Hence the answer to your
question is roughly "how far can you search?"

Under pure TDD, a failing Developer Test must implicate the most recent
edit. That's the "unit", and it could be anywhere in an application. Tests
can incidentally test modules other than the one the current suite tests.

--
Phlip
http://industrialxp.org/community/b...tUserInterfaces


Gilligan

2005-01-19, 4:20 pm

>>The last piece has to do with substitutability.
It's impossible to have code with no coupling;
the code wouldn't be able to do anything interesting.
So you have to be able to substitute testing objects
for the real production objects in order to isolate
the class (or small cluster of classes) you are
focusing your attention on at any given time.

Yes. That is what I am saying.
[color=darkred]
its own dependencies, for example by calling
the new method on a specific class to get an
instance of that class. You need some form of
dependency injection so that the tests can
inject the proper class (or instance) into it
at run time.

I don't understand. You don't call new in your code? Do you go through
a factory all of the time?


For about 3 years I worked on a significant code port from two
platforms to another. The GUI part was on Windows and went to
Macintosh. The server side on Windows went to Solaris. I was the only
guy at the company that had worked on all three platforms.

I learned to replace entire "libraries" of code by keeping the
interfaces and re-writing the internal workings. The replacement became
as simple as the link order of the build script.

About a year ago I presented this idea based on the above experience
(and I will use the terms you introduced above):
To allow substitution of real objects with testing objects you can
create the testing object exactly like the real object with the same
package structure. In the build script it will "link" in the testing
package instead of the real package.

The idea was not adopted because that was not "how they were doing
things."

At my current job I am not involved with those issues and so I do not
have the chance to use this idea. I am working on a tutorial at home,
but it will lack because it wasn't "real" and will not carry the weight
I want.

Thanks for you comments.

Geoff

Phlip

2005-01-19, 4:20 pm

Gilligan wrote:

> I don't understand. You don't call new in your code? Do you go through
> a factory all of the time?


The ideal is a top-level method, such as 'main', creates all the objects it
needs, then Sets them to each other's references. This technique is called
"Construction Encapsulation", and it only means each method takes its
objects as members and arguments. The fewer 'new' statements the better.

If you had a bunch of 'new' statements, you might refactor them into a
factory, but a better refactor is into stack objects in the calling methods.
So at boundary situations, after 'main()' finishes building the core objects
and starts the application, new data might enter the system as numbers, then
convert to objects in a factory.

All these techniques decouple, because a constructor is a pernicious point
of coupling that nails down a concrete class out of many possible
interfaces. And testing enforces this decoupling, by passing in both
customized and mock objects.

> For about 3 years I worked on a significant code port from two
> platforms to another. The GUI part was on Windows and went to
> Macintosh. The server side on Windows went to Solaris. I was the only
> guy at the company that had worked on all three platforms.
>
> I learned to replace entire "libraries" of code by keeping the
> interfaces and re-writing the internal workings. The replacement became
> as simple as the link order of the build script.


Props!

> About a year ago I presented this idea based on the above experience
> (and I will use the terms you introduced above):
> To allow substitution of real objects with testing objects you can
> create the testing object exactly like the real object with the same
> package structure. In the build script it will "link" in the testing
> package instead of the real package.
>
> The idea was not adopted because that was not "how they were doing
> things."


That idea is a pillar of the book /Working Effectively with Legacy Code/ by
Mike Feathers. He calls the place where you can change behavior a "seam".
You identified a link-time seam.

--
Phlip
http://industrialxp.org/community/b...tUserInterfaces


John Roth

2005-01-19, 4:20 pm


"Gilligan" <geoffrey.slinker@gmail.com> wrote in message
news:1106145487.268312.171870@f14g2000cwb.googlegroups.com...
> It's impossible to have code with no coupling;
> the code wouldn't be able to do anything interesting.
> So you have to be able to substitute testing objects
> for the real production objects in order to isolate
> the class (or small cluster of classes) you are
> focusing your attention on at any given time.
>
> Yes. That is what I am saying.
>
> its own dependencies, for example by calling
> the new method on a specific class to get an
> instance of that class. You need some form of
> dependency injection so that the tests can
> inject the proper class (or instance) into it
> at run time.
>
> I don't understand. You don't call new in your code? Do you go through
> a factory all of the time?


No. The point is that you have to call new _somewhere_,
and using a factory doesn't change this at all.
Wherever you call it is going to be a piece of compile time
coupling that you can't get a test around. The essence of a
good testing strategy is to make the necessary calls to
new somewhere innocuous during testing.

If you use the "pass it in as a parameter" strategy, you can
now test because you can mock the caller so it passes in
the testing instance you want instead of the production
instance.

If you use the dependency inversion strategy, you initialize
the classes by inserting the proper class references in static
variables. This is dead easy in languages like Python, Ruby
and presumably Smalltalk, it usually takes a lot of work in
languages like Java where classes aren't first class objects.
That's why dependency inversion is usually associated with
IOC containers.

> For about 3 years I worked on a significant code port from two
> platforms to another. The GUI part was on Windows and went to
> Macintosh. The server side on Windows went to Solaris. I was the only
> guy at the company that had worked on all three platforms.
>
> I learned to replace entire "libraries" of code by keeping the
> interfaces and re-writing the internal workings. The replacement became
> as simple as the link order of the build script.


That's a common strategy; see "Link Substitution" in Chapter 25 of
"Working Effectively with Legacy Code", by Michael Feathers.

> Thanks for you comments.
>
> Geoff


John Roth
>


Gilligan

2005-01-19, 4:20 pm

Thanks.

Geoff

Gilligan

2005-01-20, 3:58 pm

Ok, I get it (the new/construction issue). You are specifically talking
about the tests and for some reason I read it like this, "to make the
product code better we push the constructor calls up and up and up."
And I thought, wow, that would be terrible use of resources. The
garbage collector will never get to collect. If you use C++ and stack
variables they will never get to the destructor... etc.
Sorry, I missread.

John Roth

2005-01-20, 3:58 pm


"Gilligan" <geoffrey.slinker@gmail.com> wrote in message
news:1106244282.420820.98340@f14g2000cwb.googlegroups.com...
> Ok, I get it (the new/construction issue). You are specifically talking
> about the tests and for some reason I read it like this, "to make the
> product code better we push the constructor calls up and up and up."
> And I thought, wow, that would be terrible use of resources. The
> garbage collector will never get to collect. If you use C++ and stack
> variables they will never get to the destructor... etc.
> Sorry, I missread.


To be a little more specific, you push the new operators up
to make the code testable (or more easily testable.) It does
distort the production code somewhat (at least in some cases,
in others it improves the design) but testability is a real
product requirement: withhout testability the product isn't going
to be tested, and hence will become a bug farm.

The better strategy is to use Inversion of Control. Unfortunately,
while this is easy in Python, Ruby, Smalltalk etc, it's very
difficult in Java etc without a significant amount of infrastructure.

John Roth
>


Gilligan

2005-01-21, 3:59 am

I just happen to be studying Inversoin of Control, l1, l2, and l3,
right now. I need to learn Ruby or Python to get a feel for these
concepts.

John Roth

2005-01-21, 4:00 am


"Gilligan" <geoffrey.slinker@gmail.com> wrote in message
news:1106255893.905705.278100@f14g2000cwb.googlegroups.com...
>I just happen to be studying Inversoin of Control, l1, l2, and l3,
> right now. I need to learn Ruby or Python to get a feel for these
> concepts.


You might want to look at this one from Martin Fowler:

http://martinfowler.com/articles/injection.html

and this one from Dave (PragDave) Thomas about Ruby:

http://blogs.pragprog.com/cgi-bin/p...sparentIOC.rdoc

I don't know of a good example in Python, but a very
simple dependency injection system is not only easy to
do, it will let you take a Big Ball of Mud and get it under
test swiftly and at minimum risk of breaking anything.

John Roth
>


Sponsored Links







Also available: Server administration forum archive | Web Design forum archive | Software forum archive | Hardware reviews archive

Copyright 2008 codecomments.com