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