Wednesday, November 19, 2014

Just-For-Fun Clojure Spec DSL

TL; DR Clojure is hip for DSLs

Just for the fun of it, I've been playing around with Clojure, building a for-fun spec testing DSL (kind of like RSpec). So far, I must say, Clojure is working awesomely!

Since functions are the first-class citizen (and arguably the highest-class), it seemed natural to start off by defining ways to test them and simple values because, technically, if you can test a value, you can test the application of a function. However, testing return-value-only is not the spec testing way. Things should be descriptive.

So, here are a few examples of my spec testing DSL in action:
(describe 42
  (it > 31))

(describe +
  (when-applied-to 1 2 3 4
    (it = 10)))

The DSL is truly _that_ simple! Sure, these are simple tests, and yes, there's no nice documentation-esque output (yet). But, I just wanted to show how dead simple this DSL is at the moment.

To do the same operation in RSpec, I believe I would have to do something like:
describe 42 do
  subject { 42 }

  it { is_expected.to be > 42 }
end

describe '#sum' do
  subject { sum(*args) }

  context 'when applied to 1, 2, 3 and 4' do
    let(:args) { [1, 2, 3, 4] }

    it { is_expected.to be 10 }
  end
end

This isn't terrible, and I in absolutely no way claim this little DSL is as powerful as RSpec, but it is pretty neat, eh? Also, not to mention, the DSL is about 21 lines at this moment (whoot whoot for macros!)

Pure TDD: A JavaScript Example

The other day I took some time to practice TDD, in JavaScript. I chose a problem at random from cyber-dojo and worked my way through, as by-the-book (following Red->Green->Refactor) as I could.

Not only did I finish this exercise, but I committed each step of the process to github.

So, if you're interested in reading through an example as-pure-as-I-can-at-the-moment-TDD-session, start with this commit (I'd recommend a tool like `gitg` so that you can step through the commits easily).

Some Notes

  • The problem took me 12 Red->Green->Refactor cycles where I kept divided up a lot of the refactors into smaller steps (to keep the commits sensible).
  • At some points the algorithm got kind of bloated, but overall patterns did emerge and were easy to generalize.