
Clojure, the REPL and test-driven development - stewiecat
http://s-expressions.com/2009/07/28/clojure-the-repl-and-test-driven-development/
======
akkartik
Tests are a technique to manage programmer anxiety about code. When I feel
anxious about some aspect of my code I write a test case.

Does programming language affect level of anxiety? Definitely. Does programmer
skill affect level of anxiety? Absolutely.

Writing tests becomes more important when you're part of a team. Your choices
affect not just your anxiety but that of your teammates. That's why it's
reasonable to be more dogmatic about TDD in a team.

A lot of 'getting better at TDD' is just getting better at listening to
yourself. When I started programming the little anxieties would pile up until
I'd painted myself into a corner. With experience I pay more attention to the
little anxieties.

After some time doing TDD I feel less anxious just knowing that I can write a
test if I want. The benefit of the tests as an artifact is secondary to me;
what they primarily do is keep me from getting stressed and giving up to go
play poker.

~~~
gruseom
_Tests are a technique to manage programmer anxiety about code._

Sorry, but I think this is bullshit. If it were true, you could replace unit
testing by medication.

~~~
akkartik
Hey, I'm telling you what works for me.

And I don't have to worry about side effects from writing tests.

You're taking me too literally when I say I want to manage anxiety. If
medication is a solution, so is not hacking at all, or just remaining
inexperienced and non-introspective about my coding. Anxiety usually has good
reason, and it requires improving the _codebase_. But my metric for how the
codebase is doing is my own perception as I live and breathe it.

I have a love-hate relationship with anxiety. I need to listen to my
perceptions, because that's key to where the code comes from. But those
perceptions also have the ability to demotivate me away from the code. It
seems like an obvious connection to me ever since my experience with burnout.
Learning about the non-linearities in my motivational structure makes me a
better programmer.

Has Kent Beck influenced me? Definitely. Listening to myself works for me. I
don't see how thinking more about how I operate and how I learn my skills can
do more harm than good. Paraphrasing Sun Tzu, "Know yourself".
<http://en.wikiquote.org/wiki/Sun_Tzu>

------
gruseom
I went through the same realization and even wrote about it a few times (e.g.
<http://news.ycombinator.com/item?id=216108> and
<http://news.ycombinator.com/item?id=238770>).

Basically, the value of unit-testing everything drops considerably when you
move from OO to writing mostly side-effect free code and testing expressions
in a REPL. That doesn't mean that unit tests become worthless; it means there
are more cases where they aren't worth doing because their cost exceeds their
benefit. One area where the benefit does still exceed the cost, in my opinion,
is when you're working with complex algorithms. But that's a far cry from
test-everything.

The weakness of the test-everything school is that they don't take into
account the cost of writing and maintaining tests. They act as if the benefit
is non-trivial and the cost is trivial, which is why they think something's
wrong with people who don't agree with them. But the cost isn't trivial.

------
arohner
The other half of this article, that Clojure code requires less testing, is
also interesting. I've noticed the same thing myself. Once the author had
specified the problem, I thought of the solution in my head "he's going to map
over the epochs, rounding them to dates, and then call merge-with". Scroll
down, yep, almost exactly what I had in mind.

I don't normally do that in other languages. I attribute a large part of that
to the map/filter/reduce paradigm. Functional languages tend to have a much
more expressive vocabulary than C/C++/Java. Rather than writing a for loop to
filter an array, you just write (filter ...). The language is almost
declarative, in that you specify _what_ you want to happen, rather than _how_.

Tests are an important part of developing these days. They should count in
pg's assertion that program shortness == power. Needing to write fewer tests
to be sure of a program's correctness is an important attribute.

~~~
lispm
I would say that MAP/FILTER/REDUCE are relatively trivial applications of
Functional Programming. Add to that lack of side effects, then it gets really
simple. What would one want to test with these simple examples. But how about
more complex code, like an XML parser in a functional language. No tests? I
doubt that.

------
olliesaunders
If your writing little functions in a REPL you get to see the results of that
code immediately, in a sense, that is test-driven -- the verification aspect
is present. What isn't present is the test-first aspect that ensures your code
is easily testable, which is valuable. And you don't have the test code to
keep in a suite to run whenever to check for harmful synergies.

You could probably write a REPL with the appropriate commands that would build
rudimentary test code from lines you executed that did what you wanted them
to.

~~~
prodigal_erik
That's the best idea I've seen in a long time. Give an expr, get a value, and
if you like it, type something meaning "add a test that evals that expr and
expects the value I just saw". The only risk is convincing yourself that what
you got looks right when it's actually not.

~~~
blasdel
doctest comes with Python. You paste from REPL sessions into doccomments, and
use a little utility function to automatically find/run them.

It's not as useful as you'd think -- the only way to structure the tests is by
the class or function the comment is on, and you have to go out of your way to
avoid default __repr__ returns of the form "<object foo at 0xADDRESS>", as the
expected response handling is _ANAL_.

------
jshen
I often use tests as quasi documentation when learning someone else's code.
How does this function work? Let's check out the test.

~~~
gruseom
Yes, but I often use Lisp expressions as quasi documentation when learning
someone else's code. How does this function work? Let's call it from the REPL.
Maybe that's too complicated? Let's copy some inner bit of it to the REPL and
play with that. Ok, I see how that works, now let's try calling the whole
thing.

This 'play with some inner bit until you understand it' technique is
interesting in a couple of ways. It means you're bringing the code to life
piece by piece; and it means you're reading it inside-out. Both of those
things were revelations to me.

As an aside, this gets harder to do to the degree that the code is written in
an OO-style (even in Common Lisp) because you typically have to create other
objects before you can bring code to life, which is usually not obvious how to
do and bogs the process down.

~~~
jshen
Right, this becomes problematic with OO code. It also is problematic with
large libraries when you're trying to figure out where to start. I.e. what are
the three functions I need to call to run this search engine library.

