[TIP] A rare philosophical thought
laidler at stsci.edu
laidler at stsci.edu
Fri Aug 1 18:58:51 PDT 2008
> The Python gurus recommend unit testing to make sure code is solid.
> That's great. If I wanted to write dozens of lines of boilerplate
> code in order to make sure stuff worked, I'd have stuck with C++. I want
> to write less code and be confident in the belief that that code is
> correct and error free.
> One interpretation of this comment could be literal. I hate passionately the syntax of the standard library unit test
> framework. For me, it is indeed, a bunch of boilerplate code, with a Shakespearean English API. All of this, "Where for
> art though equality..." stuff, is a bit annoying to me. What is wrong with a plain assert?
I basically agree with you. Unittest feels *very* heavy on boilerplate to me,
which is irritating and repetitive. It's also quite abstract, which is also
a barrier to adoption - I looked at it and walked away in dismay several times
before I got to a point where I just needed to bite the bullet. And at that, the
first thing I did was write a utility module that wrapped bits of unittest in ways
I could understand.
That's one reason I really like nose: I find the barrier to adoption is much
lower, at least if all you want to use is the most basic functionality.
Now, as I got used to unittest, I have developed a couple of approaches that I
- defining a base test class to hold my collection of tests, then
inheriting from it (rather than from the unittest.TestCase object) for each
of the test cases I actually wanted to run. (Only after I figured out this
idiom did the whole structure of unittest start to make sense to me at all.)
- then sometimes defining another base test class from that, to add a
few more tests, from which another bunch of test case can inherit
- writing code to write the tests - especially if I have 100 cases on which
I want to run the same set of tests, just with a different setUp.
With nose, I can actually get generated tests, which can replace that last
idiom. But there's a tradeoff: using a generator makes the tests trivial
to write, but either difficult or impossible to run one at a time (translation:
if you can do it, I haven't yet figured out how), because the actual named
test is the generator -- the generated tests get names on the way out so
you can keep them straight in the report, but not on the way in so you can
run them one at a time. So which idiom I use depends on what I need to do.
> What makes unit test worst is that people often make custom unit test frameworks that involve non-trivial level of
> abstraction. I guess I feel like this defeats part of the purpose of testing, which is to show someone how your code
> work, not design yet another OO masterpiece.
I personally don't feel that part of the purpose of the test is to demonstrate
how your code works, but I agree about the OO masterpiece aspect. I want my
test system to conform to my code's architecture -- ie, I want to be able to
think about my tests in the same way I'm already thinking about my code. I
don't want to have to think in terms of the test system's architecture,
and then conform my code (and the way I think about it) to the test system.
>Personally I really like doctest because, while it isn't perfect, it is
> simple, and easy to understand.
Doctest makes my eyes cross - it doesn't match the way I think about TDD.
And let's not even mention string representation of floating point numbers...!
But, the one place I do like to use doctest is on any examples that I have
put into my code for documentation reasons. Usually this is the __init__,
which epydoc will use as the top-level documentation page. Doctest at least
keeps me out of the embarrassing situation of having the examples in the
documentation failing. ;)
More information about the testing-in-python