testing is easier in python

Geoff Gerrietts geoff at gerrietts.net
Fri Jan 31 13:15:18 EST 2003


Quoting Michael Hudson (mwh at python.net):
> Subject: Re: Python Success Stories or Nightmares
[...]
> Geoff Gerrietts <geoff at gerrietts.net> writes:
> 
> > Good stuff:
> [...]
> > - Unit tests are amazingly easy to write, since you can "stub in"
> >   dependencies in lots of different ways.
> 
> I recently got smacked in the face (in a good way) by this one, so I
> thought I'd quote it in isolation to make sure everyone read it.
> 

I think it's worth explaining, too, but I'm not sure this is the
thread to do it in, so I'm changing the subject line. It's worth
explaining because I've seen the topic of unit tests come up in other
threads, and I saw someone make the claim that testing is
a language-neutral solution. I don't believe that. Here's why.

I've written unit tests for Java, C, and C++ programs before. Perhaps
ironically, the C and C++ versions are probably easier to write than
the Java versions. None of the three are particularly easy to write,
though. Python makes writing unit tests easy.

When you sit down to write a unit test, you hafta look at what the
software you're trying to test, does. Sometimes, it's a pretty simple
transformation. That is, arguments go in, and outputs come out. The
only other impacting factor might be state that is internal to your
object, which you can presumably control.

  arguments --> [ big black box of code ] --> output

In the more interesting case, though, your code has additional
external dependencies. An easy example is a reliance on time.time().

  arguments --> [ big black box of code ] --> output
                   |              ^
                   v              |
                [ external lump of code ]

Now, in a testing situation, you must manage the inputs and outputs of
the external code as well as the arguments. You may be able to
envision several ways of doing this in Java or C++; the most typical
method I have seen is to create a facade object that can detect
whether you are in "test mode" or "deployment mode". In "test mode",
the facade returns a constant. In "deployment mode" it returns the
result of the actual system call.

This solution leaves a few things to be desired: it doesn't allow for
different tests wanting different constants, it adds logic to the
app-as-built, etcetera.

In Python, you can also do this, if you're the sort who likes that
kind of bondage. But Python's more flexible than that: you can also
just "replace" time.time with a stub that does the right thing.

  import my_module_to_be_tested
  my_module_to_be_tested.time.time = my_stub

Now, this does have some consequence, since it's changing time.time
for everyone. But if that's going to pickle your gizzard, you can opt
instead to write a local testing_time module, with stubs you can swap
at your leisure, and re-assign my_module_to_be_tested.time instead:
safer, with only incrementally more trouble.

In the C and C++ worlds, you can accomplish something like this by
choosing how you link your applications. It's not as elegant, not as
easy, and not as powerful, but it can be done. I am not aware of how
you can do this in Java without sacrificing the practicality of the
test environment.

Once this idiom has been mastered, applications spring up everywhere.
Now, you're not likely to want to (or /need/ to) do this in your
production code (though the timeoutsocket library uses a similar
trick), but when you're building tests or other meta-applications,
it's remarkably powerful.

Thanks,
--G.

-- 
Geoff Gerrietts <geoff @ gerrietts.net> "Many a man's reputation would not 
                                         know his character if they met on 
http://www.gerrietts.net/                the street." --Elbert Hubbard





More information about the Python-list mailing list