Unittests and external data....

mcherm at mcherm.com mcherm at mcherm.com
Wed Mar 19 16:35:11 EST 2003


Mike Meyer writes that he wants to use unit tests,
but they keep depending on external data (eg: an SQL
database). What to do?

I have been trying to address this for a while now, and I
think I've come up with an approach that works for me. If
you like to do test-driven development (write the tests
first), then I can't help you... I couldn't find a sensible
way to do that, because in the usual case I don't REALLY
trust that I understand the behavior of the external system
(the database in this example) until I've tested it. For
some reason the databases (and other systems) that I use
keep not behaving exactly according to spec (or what I
thought the spec was).

But if you've already got working code and you just want
to have tests so you can go on your merry way refactoring
things and making other unrelated changes and know that
you're not breaking anything, then I can help. The solution
that I've found is to use mock objects. (See 
http://c2.com/cgi/wiki?MockObject if you aren't familiar
with the term.)

I prefer to have a single global control of some sort
which indicates whether I'm in "unittest mode" or not. Then
all external interfaces (like databases) are accessed
through factory objects (a good idea anyhow), and I make
my factory objects aware of the "unittest mode" -- in
normal mode it may return a database Connection, but in
"unittest mode" it returns a MockConnection from the list
of MockConnections. These are set up by the unittest (before
it starts executing the code) to behave in the expected way.
I don't particularly like having the factory functions behave
differently under unittesting, but that's the best solution
I've come up with.

So my typical work process is something like this:   

 (1) Write the code for X in the first place and try it out.
 (2) Write a unittest for X which will pass (specifying how
     I expect the external data source to behave).
 (3) Move on to other things, running my test constantly
     while refactoring.

When I discover a change is needed in the call that X makes,
I do this:

 (4) Change the external source (add new stored procedure or
     whatever).
 (5) Change the code for X so it works again.
 (6) Change the unittest for X to the new behavior.
 (7) Return to regularly running the test.

I use this pattern with lots of kinds of external data sources,
databases are one example, but in my current project I have 
testing mocks for two databases, active directory, and our
config file (for tests that need particular configuration
settings).

-- Michael Chermside






More information about the Python-list mailing list