I’ve recently been working on a Clojure application that I hope to open source soon. It’s been my first experience of using Clojure, and is almost certainly one of the most thought provking things I’ve done in a long while. One of the things that is still causing me issues is how to go about TDDing Clojure applications – or rather functional programs in general.
My natural inclination – for many reasons – is to use TDD as my process of choice for developing my code. Beyond its use as a design tool, it’s having a saftey net to catch me if I screw something up. It allows me to be a little more brave, and drastically reduces the cycle between changing some code and being happy that it works. I’m used to that saftey net – I feel lost without it.
Stuart Halloway said during his Clojure talk at Qcon SF that despite being a TDD fan he finds it hard to TDD in a new language, and I get exactly what he means. A big part of it is that you’re getting to grips with the idioms, capabilities, libraries and tools associated with your new language – and a lack of this knowledge is going to impact on your ability to write good tests, let alone worry about implementing them.
Typically, when learning a new language I try and write a small application that has a real world need. BigVisibleWall was my attempt to learn Scala – but it had a real goal. With BigVisibleWall, as with my current Clojure project, I started by implementing the system by just writing the production code. I’m pushing the limits of my knowledge constantly, attempting to understand the size and shape of the solution space that I find myself in with this new tool. Once I got BigVisibleWall working with a small set of features, I broke it down and rewrote it TDD style – at that point, I had enough Scala (and I mean *just* enough) to be able to do this without it feeling like I was wading through treacle.
I consciously decided to follow the same pattern with my Clojure project. Code the main logic, get it running, then break it down and rewrite it piece by piece using TDD. But then I hit a problem – Scala and Java are similar enough languages that my programming style didn’t have to change much from one to the other. Therefore the way I structured the code and thought about TDD didn’t have to shift much. In both cases I was driving the design of an Object Oriented system. With Clojure though it wasn’t just the language which was different, it was so many of the underlying concepts were different. Put simply, I really don’t know where to begin.
My first instinct is to start decomposing functions, passing in stubs to the functions under test. But this just feels like I’m trying to shoehorn IOC-type patterns into a functional program. But what am I left with – testing large combinations of functions? That feels wrong too.
So what about you lot out there in blogland? Any other OO types trying to make the switch and encountering the same issues? Or any FP practitioners for whom TDD is second nature? Or does TDD just not fit with FP after all?
11 Responses to “Struggling with Test Driven Clojure”
I haven’t TDD in Clojure yet, but, the most similar I did was with Common Lisp and I must say that my answer to “Or does TDD just not fit with FP after all?” is “it depends”…
It depends on your problem and your knowledge on how to solve it. i used TDD in a very unfriendly legacy code project several times and it was worth to fight down the monster and place the code and tests running on a sane unit testing environment. So, for a green field project like the one you seem to be working on, I would say that it definitely pays out. Remember that you’re in it for the long run!…
I find FP more natural for tdd than imperative languages. Due to the majority of your code bring pure functions, testing it is as simple as passing in sample data and validating the output.
The hard part for me was working out when to use the repl, and when to write a test. My current rule is that I use the repl for figuring out implimentation, wheras tests drive the API of your functions.
You might be interested in watching my katacast, where I do full tdd in clojure. See it at katacasts.com
I am still going through the same problem. Don’t have answers yet, but one thing you should try is REPL more often. I have set up Slime on my emacs that lets me eval regions/buffers etc. This way there is a quick feedback about what i intend to achieve. The problem is, this doesn’t result in a bunch of tests for my automated tests, whereas TDD would have resulted in it.
Hi. I’ve just recently experienced a totally different level of “struggle” with TDD in clojure.
In looking for information on TDD in clojure, i found http://nakkaya.com/2009/11/18/unit-testing-in-clojure/. As i tried to go through the examples of using clojure.test in clojure-test-mode in emacs, i kept getting an error message about an unmatched parenthesis. It turns out you must operating in a namepace other than the default, ie (ns foo.tdd (:use clojure.test)). Spent a long agonising night banging my head against the keyboard…
That said, small unit tests for blackbox testing seems very straightforward in clojure. And really that’s what you need most. Clojure.test reminds me a lot of “e.g.” in Tcl, which i absolutely love. Just send in data, test against expected return data. I think experience with bulky, complicated unit-testing frameworks obscures the simplicity of blackbox testing in general.
[…] Newman recently blogged that he was struggling with Test-Driven Clojure: “Stuart Halloway said during his Clojure talk at Qcon SF that despite being a TDD fan he […]
[…] how do you gradually get to the solution in an FP TDD session? Sam Newman recently blogged about struggling with test-driven Clojure: “My first instinct is to start decomposing functions, passing in stubs to the functions […]
You should watch this video:
It’s a cool screencast, but it’s also the kind of problem that suits TDD fairly well – I wouldn’t have an issue here. In any case the screencast doesn’t actually demonstrate TDD, but I’m sure you knew that 🙂
[…] It took me a while to get comfortable with Clojure and to actually become able to write some tests. I had very much the same experience as my colleague Sam Newman writes about. […]
I just finished my pet project, (it’s on github: http://github.com/erlingwl/Oyster). I wrote a few comments around the testing process here (referring to your blog post):
I am also learning clj, and my sense is that it pays to minimize the functions which have side-effects. The rest of the program is a set of functions which essentially parameterize and order these side-effects according to some input (which might happen in a loop – which is the interesting case that literally your entire program is ‘recreated’ on every key click, hehe).
So you have an essential choice, to start from the bottom (the side-effect functions) or from the top (which order you want to call the side-effects from). Or do a bit of both and meet in the middle. The tests in each case are quite different – starting from the top you might stub out your side-effect functions. Starting from the bottom you create the structures you’ll need.
Something I’m trying to do more is to take a “squinty eyed look” at the problem and sketch out what I think will work, including a testing progression. FP is so powerful it can make ANY such structure work – which is kinda scary. But cool, too!
Ran across your blog while researching clojure appengine.