Fork me on GitHub
#clojure-spec
<
2020-04-13
>
Aron08:04:23

Hi, I would like to do some "spec driven development" in cljs, where should I start? Is it the same for clojure and clojurescript?

colinkahn13:04:53

I think in general it’s similar. For UI applications the boundries that you want to focus your specs on are different of course. I consider anything coming over http and websockets a good candidate for checking with spec and all of my component props as well. If you’re using Reagent the later is easy because your components are already functions and you can just us s/fdef . Another consideration is I like to keep my specs separate from my other code on the F/E. Instead I write separate spec files that require the namespaces that I’m specing. Then you can make a preloads file that imports those spec files and runs st/instrument to get development-time feedback.

Aron14:04:43

I am not using reagent because it has no support of modern react, I am using helix.

Aron14:04:24

Thanks for the note on 'separate', I was just thinking about that, I saw a video with @U064X3EF3 where it was noted to not use these in production and I am not quite sure how to do that. I thought that the compiler would remove these when I build for production and I will not have to maintain two versions of my code

colinkahn14:04:24

It looks like the defnc macro in helix eventually produces a function with [props ?ref] that you can spec like a Reagent component: https://github.com/Lokeh/helix/blob/master/src/helix/core.clj#L80-L87

Aron14:04:57

How many years before I can do what you just did @U0CLLU3QT ? : D

Aron14:04:32

I can read the code, but all these ^s and ~ confuse the hell out of me.

Aron14:04:53

and yeah, I am fairly sure I can spec it, it's more about the context/reducer situation that I am somewhat unsure. I think I can do state transition functions and spec those and then I can create spec for the state as input and output before and after the transition.

colinkahn14:04:01

The “separate” approach for me works because I agree these are more development-time tools and don’t really belong in my production source code. The compiler I believe will remove most of it (I’ve seen tricks where frameworks using Google Closure features to help with this as well) but I think certain parts of the spec library itself cannot be dead code eliminated (though don’t quote me on that, and probably better to try it yourself). For having two versions, this isn’t necessary. For example: app.components.cljs

(ns app.components)

(defn my-component [props]
  ...stuff)
app.components.specs.cljs
(ns app.components.specs
 (:require [app.components :as c]))

(s/fdef c/my-component
  :args (s/cat ...))

Aron14:04:16

I haven't even started that... : )

colinkahn14:04:00

This is the preloads feature I was referring too: https://clojurescript.org/reference/compiler-options#preloads Once you have your *.specs namespaces I make a single specs.preload or something that is roughly:

(ns app.specs.preload
  (:require [clojure.spec.test.alpha :as st]
            ; all your spec namespaces here
            [app.component.specs]))

(st/instrument)

Aron06:04:27

This is somewhat different for shadow-cljs, right?

colinkahn15:04:54

It’s pretty much the same, you’d specify the preloads in your build defined in shadow-cljs.edn

colinkahn15:04:13

Actually yeah, it’s under a different key