Re-frame subs and spec.test/instrument

Can someone shed some light to this? I’m trying to use clojure.spec.test.alpha/instrument or orchestra-cljs.spec.test/instrument to keep my re-frame subscriptions in check.

I have a re-frame subscription and a function (it totally gets called - I can see console.log showing things when component renders). I wrote an fdef that I deliberately made a wrong spec - :args is wrong, :ret - is a bad spec.

When I call instrument - it returns a vector and that fdef is listed there.

When I call that function manually - the spec fails (as expected)

However, when the sub is called from within the component - it simply ignores instrumentation.

Why would this happen? Can I “force” re-frame to validate things that need to be validated automatically? Without me having to manually call each instrumented fn?

Can you show a bit of code? Maybe the subscription doesn’t see the redefinition of the function that instrument does?

I have a normal re-frame subscription, e.g:

(require '[re-frame.core :refer [reg-sub]]
          '[orchestra-cljs.spec.test :as spec-test])

(s/fdef foo
  :ret int?)

(defn foo [] "fooo")

(reg-sub ::foo foo)

(spec-test/instrument)

The sub value gets dereffed in the component’s render fn, meaning that foo is definitely being called, but it doesn’t say anything about failing spec. If I call the function in the repl (by hand) - it fails the spec (as expected).

Does re-frame do anything special that the instrumented function gets ignored?

This might be the same problem when you have:

$ plk
ClojureScript 1.10.520
cljs.user=> (require '[clojure.spec.alpha :as s])
nil
cljs.user=> (def f (fn [x] (str "hello " x)))
#'cljs.user/f
cljs.user=> (def g (partial f "there"))
#'cljs.user/g
cljs.user=> (s/fdef f :args (s/cat :x number?))
cljs.user/f
cljs.user=> (require '[clojure.spec.test.alpha :as stest])
nil
cljs.user=> (stest/instrument `f)
[cljs.user/f]
cljs.user=> (f "foo")
Execution error - invalid arguments to cljs.user/f at (<cljs repl>:1).
"foo" - failed: number? at: [:x]
cljs.user=> (g)
"hello there"

Instrumentation of f won’t have any effect on g since it has taken the value of f and does not get any chance to see a redefinition of f.
It will work correctly when you define g as:

(def g #(f "there"))

So your re-frame instrumentation may work when you do something like (reg-sub ::foo #(foo)))

Awww… this is indeed what’s happening. I wonder if there’s a better way to deal with this. If adding specs and instrumentation means refactoring all call-sites, then I’m afraid I will have hard time selling the merits of this approach to my team

Writing (reg-sub ::foo #'foo) will likely work too.

IME trying to use var refs with spec’s fdef often doesn’t work well. YMMV

This topic was automatically closed 182 days after the last reply. New replies are no longer allowed.