I’m trying to build a macro that helps with my test fixtures. In particular, it wraps tests in database transactions. Now, doing that raw works:
(defmacro basic-transaction-fixtures
[& forms]
`(clojure.test/use-fixtures
:each
~(fn [f]
(mount.core/start
#'orca.config/env
#'orca.db.core/*db*)
(orca.db.test-util/with-transaction [orca.db.core/*db*]
(clojure.java.jdbc/db-set-rollback-only! orca.db.core/*db*)
;(do ~@forms) ;; This is what doesn't work
(f)))))
With that commented-out line it seems to work, but now I want to be able to pass in forms that will be included in the fixture. For example, I want:
(tcore/basic-transaction-fixtures
(println ">>>>I'm in the macro!") )
Ultimately I’ll have def statements in there. Anyway, uncommenting that (do !~@###$%!@forms) line (I’ve tried every variation of symbols I can think of, which clearly means I don’t know what I’m doing) doesn’t work. It spits a missing ctor error, or other things. So what should I be doing to just have it do whatever I pass before it does (f)?
EDIT the error received with the println above is:
By uncommenting that ~@ line, attempting to run the macro gives: CompilerException java.lang.RuntimeException: Unable to resolve symbol: forms in this context. Now, this might be because ~@ is undefined without a syntax-quote, right? But I also don’t understand why simply changing the
'(clojure.test/use-fixtures ...
to
`(clojure.test/use-fixtures ...
results in Call to clojure.core/fn did not conform to spec: In: [0 0] val: (orca.db.test-util/f) fails spec: ...
with or without that commented line. I could use some guidance.
One more clarification; the above was for running without a ~(fn ...) tilda statement. The error is different WITH a backtick and WITH a ~, if I have the (do ~@forms... line:
No matching ctor found for class orca.db.test_util$basic_transaction_fixtures$fn__40580
I know this isn’t answering your specific question, but is there a reason you’re using a macro for this? I’ve got fixtures that start mount components and perform database operations and they’ve seemed to work fine with just the regular higher-order function interface clojure.test exposes.
As for the question of why use a macro here, my initial answer to that was “because I’ve not done macros before and this is a great opportunity to learn.” Also, though, I thought it was the most sensible way to insert code into a db-transaction while ensuring it wouldn’t leak into long-term DB garbage. I believe that such use of macros to extend/enhance test fixtures is pretty common, but I could be wrong.