-
-
Notifications
You must be signed in to change notification settings - Fork 715
Testing
With a re-frame app, there's three things to test:
- event handlers
- subscription handlers
- views
Event Handlers are the easiest of the three because they are pure functions.
First, create an event handler like this:
(defn my-handler
[db v]
... return a modified version of db)
Then, register it in a separate step:
(register-handler :some-id [some-middleware] my-handler)
With this setup, my-handler
is available for direct testing.
Your unittests will pass in certain values for db
and v
, and then ensure it returns the right (modified version of) db
.
Event handlers mutate the value in app-db
- that's their job.
I'd recommend defining a Prismatic Schema for the structure in app-db
and then checking it after every, single event handler. Every single one.
Via middleware, this is easy to arrange. A complete example can be found in the todomvc example:
-
define a Schema for the value in
app-db
- create some middleware
- add the middleware to your event handlers
Here's a subscription handler from the todomvc example:
(register-sub
:completed-count
(fn [app-db _]
(reaction (completed-count (:todos @db)))))
How do we test this?
We could split the handler function from its registration, like this:
(defn get-completed-count
[app-db _]
(reaction (completed-count (:todos @db))))
(register-sub
:completed-count
get-completed-count)
That makes get-completed-count
available for direct testing. But you'll note it isn't a pure function. It isn't values in, values out. Instead, it is atoms in, atoms out.
If this function was on a paint chart, they'd call in "Arctic Fusion" to indicate its proximity to pure white, while hinting at taints.
We could accept this. We could create tests by passing in a reagent/atom
holding the certain values and then checking the values in what's returned. That would work. The more pragmatic among us might even approve.
But let's assume that's not good enough. Let's refactor for pureness:
First step in this refactor, is to create a pure function which actually does ALL the hard work ...
(defn completed-count-handler
[db v] ;; db is a value, not an atom
..... return a value here based on the values db and v)
The second step in the refactor, register using a thin reaction
wrapper:
(register-sub
:completed-count
(fn [app-db v]
(reaction (completed-count-handler @app-db v))))
completed-count-handler
is now doing all the work. That's what we'll want to test. And it is a pure function. I think we are there.
Views are the trickiest.
If a View has a Form-1 structure, then it is fairly easy to test.
A trivial example:
(defn greet
[name]
[:div "Hello " name])
(greet "Wiki")
;;=> [:div "Hello " "Wiki"]
So, here, testing involves passing values into the function and checking the data structure returned for correctness.
What's returned is hiccup, of course. So how do you test hiccup for correctness? Remember it is just a clojure data structure - vectors containing other vectors and maps, etc. Perhaps you'd use https://github.com/nathanmarz/specter to check on the presence of certain values and structures? Maybe.
But what if the View has a subscription (via a Form-2 structure)?
(defn my-view
[something]
(let [val (subscribe [:query-id])] <-- reactive
(fn [something] <-- returns the render function
[:div .... using @val in here])))
At this point, I don't have any tricks up my sleeve. I don't have a "pure" way to test this.
My only suggestion is to first setup app-db
... then, in your test, call my-view
, and then call the returned value (because my-view is a function returning a function), and then check the value returned.
Which is all possible, but messy.
So we've stumbled a bit at this final hurdle, but prior to this, the story was good.
Deprecated Tutorials:
Reagent: