Testiness»
Rafe asks about
blogger's test-driven development experiences. I've been messing around
with XP on-and-off for the last
month or two. Yoz and I are doing some
long-distance pair programming via TightVNC, Leo, and Yahoo Messenger Voice Chat, which has
been slow-going but fun. I haven't used XP or unit-testing for large projects,
but I've been thinking a lot about my experiences.
Unit testing is definitely the easiest part of XP to grasp, but the XPers
have a point when they say that much of the benefit of writing tests before
you code comes from synergies from XP's other practices. One of the reasons
why most of us don't write as many tests as we should is because of the
limited returns. You spend a long time writing little fragments of code, which
only come into their own if a bad bug trips them up in a predictable way.
Anyone who has got caught in the trap of writing test code to catch bugs, only
to find themselves fixing bugs in the test code, knows that tests can be a
real drag on the flow of programming.
Unit testing before you code ups that return on investment. Firstly, while
you're writing the test code, you're also mentally fleshing out the interface
and algorithms of the future class itself. Many times now, I've found myself
wrestling
- and solving - some of the biggest algorithmic problems I have to face in
writing a method while I'm writing the early test code. This has a couple of
effects: psychologically, coding up the tests seems much less of a waste of
time. It feels like a useful warm-up to the final work, rather than a
finickety post-facto dredge over your previous work.
More practically, it means that the final product itself is far less
composed of the lumber of a a dozen experiments. The effectiveness of this
is particularly true in XP's iterative design process. There's a minimal
amount of upfront design in XP, but there's design *somewhere*. One of the
hidden places design gets done is throughout the testing process. Now, that
may horrify you, but it definitely means there are rewards to writing tests
first that may not exist in a less agile development environment.
It has another knock-on effect, which, I think, leads to more productive
and pleasant testing. When I'm writing unit tests these days, I feel a strong
pressure not to "code to the test".
The initial temptation when first writing pre-code tests is to write the
minimum needed for your code to work. For instance, the code we're writing at
the moment is a simple WAP client. We have a test which just "presses the
button" on the WAP page, to see whether we're actually activating a CGI
script. Putting aside whether this is really a unit test or a functional test
(I admit to not truly understanding the difference), our first bash at this
had something along the lines of:
def testButtonPressable(self):
full_url = self.wap_dir + self.formurl() # get url of form
params = urllib.urlencode({'test' : 1}) # set form with "test=1"
f = urllib.urlopen(full_url, params) # open the CGI script
return self.assert_('<!-- TEST OK -->\n' in f.readlines())
Yoz and I discussed this for quite a while. I felt uncomfortable. It
seemed to me that the test added code to the final version, but the wrong sort
of code. It just adds to the main method code which fulfills the test's
requirement, and little else.
I was a bit surprised by my reaction to this, but realised that it's partly
what XP teaches you: because you're outlining the skeleton of your later
design in your tests, code like this feels like wasted effort. The test
sketches the outline of code, but it's not code that's a step toward your
final end product.
In the end, Yoz won this argument, and did so by pointing out, I think,
one of the problems of unit testing the XP way. XP emerges, in large part,
from Smalltalk best practices. One of the great features of Smalltalk is that
you're working in a completely OOP environment. Access and introspection just
aren't much of a problem. If there's an object out there, you can talk to
it.
No matter how OOPified our current programming world is, that's just not
the case, and that poses problems for unit testing with suites like JUnit. Take a look at the example
above. One way that we could improve the design of the code, and write a test
that fits in with the main execution flow is something like this:
def testButtonPressable(self):
full_url = self.wap_dir + self.formurl()
params = urllib.urlencode({'test' : 1})
f = urllib.urlopen(full_url, params)
cgiobject = CgiObjectSingleton()
return self.assertEquals(cgiobject.form['test'], 1)
Here we really are testing something, without adding additional code. We're
seeing if opening the form's submit URL passes data to our cgi code. To do
that we get hold of the object that represents the CGI codes state, and peer
in it to see if it received the form data.
Unfortunately, this approach is impossible without quite a bit of wrapper
coding: the CGI script in our world is in a completely different process
space, and lives and dies without being easily introspected by our test code.
We could write a persistent CgiObject communication interface, but you
wouldn't normally do so when writing CGI code without a unit test.
This seems a random example, but I've hit the same problem in other places
as well. Testing WxPython GUIs using PyUnit is difficult, for instance,
because somehow you've got to interpose your tests into your Python app's
event loop. I have funky code to do this now, but it required some heavy extra
coding. Unless you're really enthusiastic about writing your own test
framework code, such test framework hacking has got to feel like wasted
time.
It goes without saying that in Smalltalk you hit these problems far more
rarely. You're all sitting in the same event loop, and all instantiated
objects and classes are accessible.
Even with these problems, though, I still write my tests before my code.
Why? Well, part of the reason is I rather like Smalltalky OOP: having
easily examined objects I think does lead to better code, and it's nice to
have that kind of architecture fall out of the test code you're writing. One
of the recurrent problems with CGI code, for instance, is that it's
really tempting to write it in a procedural style. Splitting it up into
individual objects makes things a lot easy, and unit testing helps build that
discipline.
The other reason is, as the phrase goes, I've become "Test
Infected". My tests have now caught so many bugs, small or large, that it
feels rash to write code without writing the tests first.
My infection was unconnected to XP practices. The first program I wrote
tests for was the code that handles my mail archiving. I really, really,
really didn't want this to fuck up, so I carefully pre-wrote all my tests.
Since I started using the code (about 700 lines of Perl), I've only hit one
bug. I found the problem almost instantly by kicking up the test suite again.
It turned out that the syntax of the GNU 'date' command had changed subtly
between versions. It's the sort of bug that would have taken days to track
down without the testsuites.
My own feeling is that, if you're uncomfortable with JUnit, just write
tests the way you feel comfortable with. After a while, when you've received
some outright benefits from using tests, you'll feel more encouraged to get to
grips with a more alien suite. In the end, the *Unit classes are just a fancy
way of saying: test this, then this, then this, then this. And that's all you
need. For small programs these days, I'm spending more time in Python's doctest
.
2003-02-05»
Bees In Bonnets»
About seven months ago, I was gibbering in my Sunday Times column about
Internet pressure groups. I rather glibly promised to give links to anyone who
mailed me with info about their own single issue site. I've now dug up the
mailbox that contained all of those URLs. Gritting my teeth with the shame of
being so late, here are those links. No guarantee of quality here - they're
just people who wrote in. It's spoor for the googlebot, mainly.
Somewhere to go if you're a descendant of the
Bond-Jones family of Liverpool.
A slightly strange free online test for heart trouble., together
with a history of the Norfolk
and Norwich hospital and the unsuccessful fight for its survival.
A kinda warbloggy, kinda not Irish blog.
A site by Brian Barder which, as he says, "absolutely buzzes with multiple
bees from my pinkish bonnet". It's sort of JerryPournelle.com mixed with Samizdata.net in the style of David Brake's Weblog. Also, possibly, Jimmy Young.
A site about looking after
children's teeth.
A parody of National Westminster
bank.
A site that lets you add your own entry to the Band Family Tree.
And Greenhouse Gas News, which
does exactly what it says.
2003-02-04»
The Story behind FaxYourMP»
I've had a rant building up about this for some time, but Stef's always
been better at the
nuclear-tipped flame than me.
We've running FaxYourMp.com
for two years now, always in the expectation that Parliament would eventually
introduce their own system for making representatives more easy to contact.
Not only has that not happened - they've now started filtering
constituency mails. Their obscenity filter has caught constitutent mails
about the upcoming Sexual Offences Act, and a party position paper on - of
course - censorship.
Stef has all the facts on
faxyourmp.com, and more. It's really worth reading, both as an examination
of where e-government is now, how far it has to go. And how easy it could be
to get there.
One of the most heartening aspects of the service has been the letters that
constituents write. Almost without exception, the letters they send MPs are
reasoned, well thought out, not always perfectly spelled, but often insightful
engagements in civic society. Far from the image of a disaffected and
disengaged electorate, we see a mass of people who've discovered that they can
effectively participate in democracy- if artificial and archaic barriers are
not put in their way.
I can't be the only one who sees the irony in the fact that in the week that
we celebrate 50K faxes served, the people whose job should be doing what we do
for free are still trying (feebly) to raise barriers against the citizenry.
There's a dramatic mindset change that needs to happen over there, because, if
you will not give us greater democracy, we will simply take it from you.
WINE trouble»
Looks like WINE, the Windows non-emulator for Linux, is going to have some
problems making the shift to glibc2.3. WINE has its own implementation of
threads, which glibc doesn't know about. Part of the act of splicing this
implementation into the old glibc involved gently pursuading the library to
look in a different location for system error result codes. That hack doesn't
work in the new glibc.
It looks like the solution may be to port WINE to libc threads, which is a
bit terrifying. Previously, it's been out of the question, because there just
wasn't a good match between pthreads system and the Windows model. A
combination (I think) of the new kernel's thread implementation and
improvements in pthreads itself may fix that, but it's still a big leap.
If WINE does make the move, a few other bonuses fall out of the work.
Firstly, Mono, the Unix port of .NET, would be able to re-use
WINE code for its graphics and UI libraries. And because WINE would no
longer be bbolting strangeness onto glibc, the project would be
able to use Valgrind, the open-source memory debugger
for x86-Linux.
There are political ramifications also. The WINE project is in a curious
configuration at the moment, with several groups keeping their own mildly
forked versions (either for business reasons, or because of disagreements over
licensing). They're all going to have to come together to co-operate with this
- and in pretty smart order, because Redhat ships with the new glibc in a few
months.
These are the kind of shifts that often utterly devastate private
programming projects. They can be pretty stressful for open source endeavours
too. If you are at all curious how free software copes with major logistical
challenges, this would be a good project to watch.