clojurescript 2025-06-25

I am considering porting my clojure-only test framework #lazytest to Clojurescript. I don't have a ton of experience with Clojurescript's compilation model or runtime (or lack thereof?), so are there any resources on such topics?

just look at the cljs.test port in ClojureScript

the finicky part is the async support

I wouldn't recommend looking at cljs.test. its kinda of a horrible port and way too macro heavy when it doesn't really need to be

depends on what you mean by "doesn't need to be" lol

well, for one you will need to "collect" your tests somehow, maybe filter them and then run them

the collect parts, as in find the vars/metadata must be a macro. the rest can just be functions. cljs.test, for example run-test complects those together, so everything is a macro, and everything it wants to use thus also becomes a macro

so, if you keep them separate you can make your life easier 😛

if you want an example how thats handled in shadow-cljs then there is only this macro for finding all the tests https://github.com/thheller/shadow-cljs/blob/master/src/main/shadow/test/env.clj

not perfect either but all those damn macros make it very hard to work with cljs.test from the tool side 😛

interesting. in lazytest, all of the macros exist for defining the test suites and test cases and assertions. everything else (finding logic, the runner, filtering, printing, collecting results) is plain functions

i'll have to read your code to see what direction to take with the cljs version

yep, thats the same. this just uses cljs.test/deftest as normal for defining things

👍 1

if you have a similar macro and that just registers each test with some place other than metadata, then you already have everything you need

nah, it's all var metadata 🥲

as in something like (do (defn this-is-the-test [..]) (your.test.thing/register "the.name.or.something" this-is-the-test {:extra "data"})) or so emitted by the macro

that stuff you could lookup fine at runtime if that just puts it into an atom or so

completely fine to do for CLJ as well

i'd prefer to stick with vars because that keeps side effects low

using the var collection and modifying it vs your own collection doesn't seem like more side effecty to me, but 🤷

well, with vars you have the support of all existing clojure functionality, which i guess keeps it from clojurescript

you can reload, you can rebind or alter, you can pass around as a first class object, you can inspect namespaces, etc

and all tooling and editors already support them

when i was looking for clojure.test alternatives, i looked at midje and speclj but neither is based on vars so you can't do repl driven development

the cljs compiler converts cljs -> js. something else actually runs it. the only limitation this implies is in how macros are handled, so they run in CLJ during compilation still, but can't have interspersed runtime access and evaluate or access those parts.

right, i more mean that (for example) there's no vars in clojurescript, so you can't say (require 'some-namespace) (ns-interns (the-ns 'some-namespace)) to get the public vars of a namespace, which fundamentally changes how you might approach writing a framework (like lazytest)

true, but you can get most of the similar data via the cljs.analyzer.api helper functions

hah perfect. is there documentation on that stuff somewhere? or an article on it or do i just have to read the cljs code directly?

I'd assume the api docs are available somewhere, but not actually sure. also this will be CLJ code 😉

but the API is intentially designed to look like their CLJ counterparts, so the usual all-ns ns-publics etc are all there

i'm reading through now, thank you

Is there some way to reify js interfaces? I'd want to mock localStorage in tests by creating a js/Storage instance, but just reifying that with the correct methods doesn't seem to work (results in method is not a function errors)

no such thing as JS interfaces really. typically just an object with functions is enough. (def mystorage #js {:setItem (fn [key val]) :getItem (fn [key]))

or (deftype MyStorage [] Object (setItem [this key val]) (getItem [this key]))

is there some advantage to either of those approaches?

one has the implicit constructor/this, the other is just two functions I guess

(deftype MyStorage [data]
  Object
  (setItem [this key val]
    (unchecked-set data key val))
  (getItem [this key]
    (unchecked-get data key)))

(def mystorage (MyStorage. #js {}))