I wrote a very small little DSL for spec testing:
(ns funky-spec.core)
(def described-entity (ref nil))
(defn it [fun & v]
(assert (apply fun (cons (deref described-entity) v))))
(def it-is it)
(def it-is-the it)
(defmacro describe [value nest]
`(dosync (ref-set described-entity ~value)
~nest))
(defmacro when-applied [nest]
`(dosync (ref-set described-entity ((deref described-entity)))
~nest))
(defmacro when-applied-to [& args-and-nest]
`(dosync (ref-set described-entity (apply (deref described-entity) (take (dec (count (quote ~args-and-nest))) (quote ~args-and-nest))))
(eval (last (quote ~args-and-nest)))))
This turned out to be very nice, for writing some simple, concise specs:
(ns funky-spec.core-test
(:require [clojure.test :refer :all]
[funky-spec.core :refer :all]))
(describe 42
(it = 42))
(describe 99
(it not= 42))
(describe 0
(it-is zero?))
(describe 99
(it-is-the (complement zero?)))
(defn answer [] 42)
(describe answer
(when-applied
(it = 42)))
(describe identity
(when-applied-to 99
(it = 99)))
(describe +
(when-applied-to 42 35
(it = 77)))
(describe +
(when-applied-to 42 35 9 10
(it = 96)))
I'm new to Clojure and macros so any thoughts on this code would be appreciated. I have a few concerns that you could start with:
when-applied
could (I believe) be an alias forwhen-applied-to
. I can't get this to work withdef
, is there something I am missing?- Is there a better pattern I could use other than a "global"
ref
for thedescribed-entity
? I tried usinglet
and shadowing, but couldn't get it to work with macros - In
when-applied-to
, there's a lot of quoting and aneval
, I did this to keepit
from pre-maturely executing. Is there a cleaner way to thunkit
?