(A Tragedy of My Own Devising)
(And helpful it was… I see plenty of mistakes I made and summarize the lessons learned at the end).
First a quick background. (defdrink) will be/is a web application which will assist in the choosing of a cocktail based upon the contents of the users liquor cabinet. I am using this project as a means of learning Clojure. I have put all the source on github (as one does…) and in this post will refer to particular commits to illustrate different steps.
In the Beginning…
At the point where our story begins I had slapped together a quick (and ugly) prototype. The purpose of the prototype was to get a Clojure project which has a web page which allowed retrieving and saving data into a database; all served up on Heroku. Being a prototype - it had not tests (horrors). This moment in time is commit 0da77fbafc87634e94e0342e409e5b071b8d6c1f.
Trying to put in the first Midje test.
It took me two tries to get my first Midje test (and that first test merely
proved that Arithmetic worked:
(fact (+ 2 2) => 4)).
My first attempt was me doing what I thought was ‘obvious’ and what I thought
the Midje wiki was telling me I should be able to do: put a bit of magic in my
projects.clj file and then a test file with a
(fact ...) in it.
That attempt ending with frustration at the fact the only feedback I was getting was a bunch of Java stack traces.
My second attempt was simply making these same stumblings - I had wrongly assumed that I had I simply not quite understood the previous time.
After I realized that my second attempt was heading toward another failure I finally rolled back and tried to take the next smallest step. I wanted to run Midje (with Lazytest) and get it to report something like “0 tests”. With the help of the lein-midje plugin Readme I got this working in not too much time. If you look at that commit you will notice a glaring error - one that was not obvious to me as I tried to continue.
My first Midje test…
A Problem of My Own Devising
Ages ago when I started this project I ran
lein new defdrink and then, since
I didn’t have any tests, I removed the
test subdirectory. Now, a month
later, I couldn’t remember just how the test directory was supposed to be laid
out, and it was obvious to me that it needed to have some special directory
tree/naming to find the files etc. Mixed into that was a lack of understanding
Clojure namespaces and directory structures (this was just me being dense).
At this point I kept getting stack traces about not finding namespaces, which
all seemed right - but obviously lein, midje, clojure or all three didn’t
Finally I had just ran
lein new foo in a temp directory and saw the
structure needed. That was the clue I needed – the clue I couldn’t find
this little simple piece of information out on the web either for Midje or
Clojure’s own test framework. Now that I had the directory structure right,
the namespace in the file right it would work right? Right?
As I am sure all of you playing along at home can see - I never added a dependency on Midje itself in the previous step. That means at this point I started getting messages that said:
Could not locate midje/sweet__init.class or midje/sweet.clj on classpath
Let me tell you this was very confusing after all I knew that I already had Midje working. Now that I added a test it says it can’t find Midje?! Finally I saw my error and fixed it. Now at long last, I had a Midje test! Of course it was just a dummy test but it was actually running.
Now, let’s do it for reals…
My next step was to get a real test. My premise for these tests was to remove
some hardcoded SQL code in my model class. I was going to push that code down
into a couple helper classes so that I could isolate myself from the database.
The first test was to show that my
defdrink/all method would delegate down
to my new
sql/select method with the
:drinks parameter (that is the
I once again had some odd and annoying problems with the only feedback being cryptic stack traces. I don’t remember all the details but they involved statements such as ‘provided not being defined in this context’ - which led me down wild chases to determine if I had all the right modules required etc. Fortunately/unfortunately the problems resolved themselves (I hate that) and I got my first real test running:
(I sort of like how it turned out… I like the metacontstants feature and am excited to try it out more.)
And now for my encore…
Now that the select statement was sequestered, I turned my attention to the insert statement. This is where I again ran into problems.
This new test, like the previous, was going to be a simple test of delegation.
Did the model layer properly call down to the database layer to query or
persist the data. The test I wanted to write was to say that the
defdrink/save method called the
sql/insert method with the correct
parameters. My first attempt at this ended… (wait for it…) in
What’s wrong THIS time?!
In Ruby or Java/Groovy I’d simply have my test call my method and test the
expectation on my mock object that the
sql/insert method was called with the
correct parameters. Unfortunatly for me, I could not figure out how to do that
in Midje. I ended with the following unhappy test:
1 2 3
I’ll freely admit that while trying to write this test I began to have doubts (which I still hold) that perhaps I shouldn’t even by trying to right this test. It just seemed like a code smell to me. The way the test ended up seems to be telling me: “Mark, just what are you thinking?!”
In conclusion (finally) my problems were much of my own devising. Whether it was shooting myself in the foot, trying to do something quickly because “its easy right?”, not yet understanding this language/environment I am trying to work in, or simply because I am trying to write Clojure tests as I would Ruby or Java tests – I’ve had a hard time getting to this point.
Even admitting my own problems, I have to say, IMNSHO that it would have been nice to see a tutorial/cookbook out there of how to get started with Midje. What I’d have liked to have found would be something which stepped me through:
- what to put in my
project.cljfile for midje, lazytest, and lien
- a simple example of getting one’s first tests off the ground
- and then a bit more showing mocking for example.
In researching this blog post I see I did overlook some resources on the Midje wiki that would have helped, at least with the second two points.
So the moral is:
- slow down
- small steps
- read the docs, not just look at them.
I do think I might try to contribute this sort of tutorial to the Midje project. Because I can’t be the only dope who thinks he knows what he’s doing, who will get frustrated because there is not a tutorial to hold his hand through the first steps.