Skip to content

Commit

Permalink
Macro helpers for repetitive operations when writing tests
Browse files Browse the repository at this point in the history
  • Loading branch information
heralden committed Oct 21, 2019
1 parent e2001aa commit a19cd54
Show file tree
Hide file tree
Showing 4 changed files with 137 additions and 98 deletions.
5 changes: 3 additions & 2 deletions project.clj
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@
["cljsbuild" "once" "min"]]
"deploy" ["with-profile" "+uberjar" "deploy" "clojars"]
"format" ["cljfmt" "fix"]
"kaocha" ["with-profile" "+kaocha" "run" "-m" "kaocha.runner"]}
"kaocha" ["do" "clean"
["with-profile" "+kaocha" "run" "-m" "kaocha.runner"]]}

:repositories {"clojars" {:sign-releases false}}

Expand All @@ -62,7 +63,7 @@

:profiles {:dev {:dependencies [[binaryage/devtools "0.9.10"]
[day8.re-frame/re-frame-10x "0.4.4"]
[day8.re-frame/tracing "0.5.1"]
[day8.re-frame/tracing "0.5.3"]
[figwheel-sidecar "0.5.19"]
[cider/piggieback "0.4.1"]]
:plugins [[lein-figwheel "0.5.19"]]}
Expand Down
55 changes: 21 additions & 34 deletions test/cljs/im_tables/core_test.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -24,42 +24,29 @@
(:id vocab)))}}})

(deftest load-im-tables
(run-test-async
(let [loc [:default]]
(rf/dispatch-sync [:im-tables/load loc im-config])
(wait-for [:main/initial-query-response]
(testing "im-table runs query"
(let [loc [:default]]
(utils/after-load loc im-config
(testing "im-table runs query"
(let [response @(rf/subscribe [:main/query-response loc])]
(is (some? response))))))))
(is (some? response)))))))

(deftest column-summary
(run-test-async
(let [loc [:default]]
(rf/dispatch-sync [:im-tables/load loc im-config])
(wait-for [:main/initial-query-response]
(rf/dispatch-sync [:im-tables.main/init loc])
(wait-for [(utils/match-times {:main/save-column-summary 6
:main/save-decon-count 3})]
(testing "at least one non-empty column summary"
(let [summaries @(rf/subscribe [:summaries/column-summaries loc])]
(is (some (every-pred map? not-empty)
(map :response (vals summaries)))))))))))
(let [loc [:default]]
(utils/after-init loc im-config
(testing "at least one non-empty column summary"
(let [summaries @(rf/subscribe [:summaries/column-summaries loc])]
(is (some (every-pred map? not-empty)
(map :response (vals summaries)))))))))

(deftest sort-column
(run-test-async
(let [loc [:default]]
(rf/dispatch-sync [:im-tables/load loc im-config])
(wait-for [:main/initial-query-response]
(rf/dispatch-sync [:im-tables.main/init loc])
(wait-for [(utils/match-times {:main/save-column-summary 6
:main/save-decon-count 3})]
(rf/dispatch-sync [:main/sort-by loc "Gene.primaryIdentifier"]
(wait-for [(utils/match-times {:main/save-column-summary 6
:main/save-decon-count 3})]
(testing "response can be sorted by column"
(let [response @(rf/subscribe [:main/query-response loc])
result (get-in response [:results 0])]
(is (= "1" (->> result
(filter #(= (:column %) "Gene.primaryIdentifier"))
first
:value))))))))))))
(let [loc [:default]]
(utils/after-init loc im-config
(rf/dispatch-sync [:main/sort-by loc "Gene.primaryIdentifier"])
(utils/wait-for-query loc im-config
(testing "response can be sorted by column"
(let [response @(rf/subscribe [:main/query-response loc])
result (get-in response [:results 0])]
(is (= "1" (->> result
(filter #(= (:column %) "Gene.primaryIdentifier"))
first
:value)))))))))
113 changes: 113 additions & 0 deletions test/cljs/im_tables/test_utils.cljc
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
(ns im-tables.test-utils
(:require [day8.re-frame.test :refer [run-test-sync run-test-async wait-for]]
[re-frame.core :as rf]
[clojure.string :as string]
#?(:cljs [cljs.core.async :refer [chan put!]]))
#?(:cljs (:require-macros [im-tables.test-utils])))

;; Tips for writing tests:
;; https://github.com/intermine/bluegenes/blob/dev/docs/developing.md

;; Remember to activate the fixtures in your testing namespace if you're
;; going to use exports from this namespace:
;;
;; (use-fixtures :each utils/fixtures)

#?(:cljs
(defn stub-fetch-fn
"We often want to stub imcljs.fetch functions using with-redefs. Instead of
having to define a function to create, put and return a channel, call this
function with the value you wish returned and it will do it for you."
[v]
(fn [& _]
(let [c (chan 1)]
(put! c v)
c))))

#?(:cljs
(def stubbed-variables
"Add functions that reset any globally stubbed variables to this atom.
(swap! stubbed-variables conj #(set! fetch/session orig-fn))
A fixture will run all these functions and empty the atom before tests."
(atom '())))

;; This will be used to clear app-db between test runs.
#?(:cljs
(rf/reg-event-db
:clear-db
(fn [_db] {})))

#?(:cljs
(def fixtures
"Necessary fixtures to use the exports from this namespace.
Use by calling use-fixtures from your testing namespace:
(use-fixtures :each utils/fixtures)"
{:before (fn []
(when-let [vars (seq @stubbed-variables)]
(doseq [restore-fn vars]
(restore-fn))
(reset! stubbed-variables '()))
(rf/dispatch-sync [:clear-db]))}))

(defn- events-matched?
[matches matchm]
(every? (fn [[event-id times]]
(= (get matches event-id) times))
matchm))

(defn match-times
"For use inside `(day8.re-frame.test/wait-for [(match-times matchm)])`.
Expects an event-id matching count map as argument:
{:main/save-column-summary 6
:main/save-decon-count 3}
Waits until all event-id keys in the map have occurred the exact amount of
times specified as the value. Useful when you know a chain dispatch can fire
an X amount of requests, and you just have to wait until they all finish."
[matchm]
(let [matches (atom nil)]
(fn [[event-id]]
(events-matched? (swap! matches update event-id (fnil inc 0)) matchm))))

(defn deconstruct-naively
"Works similarly to `imcljs.query/deconstruct-by-class` except it doesn't
need a model to deconstruct. In addition, it returns only a hash set of the
unique classes instead of a map with more data. For when you need a solid
guess at the amount of deconstruction operations for a query."
[{:keys [from select] :as _query}]
(let [full-paths (map #(if (string/starts-with? % from)
%
(str from "." %))
select)]
(into #{}
(comp (map #(string/join "." (drop-last (string/split % #"\."))))
(filter some?))
full-paths)))

;; Here are some simple macros that expand to code you'll often need to write
;; in tests. By grouping them under a single name for each operation, the unit
;; tests can be written more succinctly, making their intent clearer.

(defmacro wait-for-query
"Used to wait-for im-tables-3 to complete a query."
[_location im-config & body]
`(wait-for [(match-times {:main/save-column-summary
(count (get-in ~im-config [:query :select]))
:main/save-decon-count
(count (deconstruct-naively (:query ~im-config)))})]
~@body))

(defmacro after-load
"Common operation that includes loading im-tables-3."
[location im-config & body]
`(run-test-async
(rf/dispatch-sync [:im-tables/load ~location ~im-config])
(wait-for [:main/initial-query-response]
~@body)))

(defmacro after-init
"Common operation that includes loading and initialising im-tables-3."
[location im-config & body]
`(after-load ~location ~im-config
(rf/dispatch-sync [:im-tables.main/init ~location])
(wait-for-query ~location ~im-config
~@body)))
62 changes: 0 additions & 62 deletions test/cljs/im_tables/test_utils.cljs

This file was deleted.

0 comments on commit a19cd54

Please sign in to comment.