I have an async Pedestal interceptor I want to test:
(def my-interceptor
(io.pedestal.interceptor/interceptor
{:name :my-interceptor
:enter (fn [context]
(as/go
(do
(Thread/sleep 1000)
(assoc context :answer 42))))}))
I first tried a naïve test:
(deftest my-test
(is (= 42
(:answer (io.pedestal.interceptor.chain/execute {} [my-interceptor])))))
This doesn’t work because chain/execute returns nil when it has async interceptors. I tried another solution adding the test in an interceptor just after the tested one:
(deftest my-test
(io.pedestal.interceptor.chain/execute
{}
[my-interceptor
(io.pedestal.interceptor/interceptor
{:name :test
:enter (fn [context]
(is (= 41 (:answer context))) ; should fail
context)})]))
However this doesn’t work because the test terminates before the test is executed, and thus succeeds… even if the test fails a second after:
Ran 1 test containing 0 assertions.
No failures.
FAIL in (my-test) (somefile_test.clj:49)
expected: (= 41 (:answer context))
actual: (not (= 41 42))
In practice my tests suite (using Kaocha) fails because there’s a deftest with no assertion in it.
Given that chain/execute returns nil and not a chan, I can’t wrap it in a as/<!! to block until it terminates.
At this point I’m stuck. Is there anything I can do to test that kind of interceptors?
How about this approach?
(require '[clojure.test :as test])
(require '[clojure.core.async :as async)
(test/deftest async-test []
(let [c (async/chan)]
(future (println "Running mah interceptors") (async/>!! c :done))
(test/is (= :done (async/<!! c)))
(async/close! c)))
That runs the actual interceptor code in a separate thread. Test code just needs to post something to the c when it is done.
Related
I want to check if the right instance is used.
But while the repl gives me true, the actual test returns nil.
Idk why
(ns my-app.queue)
(def queue (atom clojure.lang.PersistentQueue/EMPTY))
(ns my-app.queue-test
(:require [my-app.queue :as sut]
[clojure.test :refer :all]))
(deftest queue-type-test
(is (instance? clojure.lang.PersistentQueue #sut/queue)))
;repl output
;(instance? clojure.lang.PersistentQueue #sut/queue) ;=> true
The command lein test gives me:
FAIL in (queue-type-test) (queue_test.clj:6)
expected: (instance? clojure.lang.PersistentQueue (clojure.core/deref sut/queue))
actual: nil
I know the test itself is not really useful, but I can't figure out why this behaves like this.
tl;dr: both is and # are macros. Most obvious fix would be something like:
(deftest queue-type-test
(let [queue #sut/queue]
(is (instance? clojure.lang.PersistentQueue queue)))
clojure.test docs don't make it immediately obvious, but is is a pretty tricky macro that looks inside its body. Here's a snippet from the docstring:
user> (is (= 5 (+ 2 2)))
FAIL in (:1)
expected: (= 5 (+ 2 2))
actual: (not (= 5 4))
false
Notice actual: (not (= 5 4)). It's clear that it did not evaluate = so that it can show us the result of (+ 2 2). Here are all the special cases for assert-expr multimethod.
If any step in my test setup fails, I want to report this as a failure, and cease any subsequent tests in the current deftest block (or current namespace). One way to do this now:
(if some-condition-is-ok
(do
... do tests)
(is (= 1 0) "Failure, condition not met")
The above:
Reports failure if some-condition-is-ok is not met
Does not run any tests, since the setup condition was not met
Except that it does not flow well, and does not work well for multiple conditions. I'd like something like:
(let [;; setup here...]
(assert-or-stop-tests some-condition-is-ok)
... continue with tests here
Any ideas on a clean way to do this?
You could use Mark Engelberg's better-cond for this:
(require '[better-cond.core :as b]
'[clojure.test :refer [is]])
(def some-condition-is-ok true)
(def some-other-condition-is-ok false)
(deftest a-test
(b/cond
:let [#_"setup here..."]
:when (is some-condition-is-ok)
:let [_ (is (= 0 1))]
:when (is some-other-condition-is-ok)
:let [_ (is (= 1 2))]))
Or if you want to avoid the :let [_ ,,,], you could define your own macro:
(defmacro ceasing [& exprs]
(when-let [[left & [right & less :as more]] (seq exprs)]
(if (= :assert left)
`(when (is ~right)
(ceasing ~#less))
`(do
~left
(ceasing ~#more)))))
(deftest b-test
(let [#_"setup here..."]
(ceasing
:assert some-condition-is-ok
(is (= 0 1))
:assert some-other-condition-is-ok
(is (= 1 2)))))
I am implementing an app using Stuart Sierra component. As he states in the README :
Having a coherent way to set up and tear down all the state associated
with an application enables rapid development cycles without
restarting the JVM. It can also make unit tests faster and more
independent, since the cost of creating and starting a system is low
enough that every test can create a new instance of the system.
What would be the preferred strategy here ? Something similar to JUnit oneTimeSetUp / oneTimeTearDown , or really between each test (similar to setUp / tearDown) ?
And if between each test, is there a simple way to start/stop a system for all tests (before and after) without repeating the code every time ?
Edit : sample code to show what I mean
(defn test-component-lifecycle [f]
(println "Setting up test-system")
(let [s (system/new-test-system)]
(f s) ;; I cannot pass an argument here ( https://github.com/clojure/clojure/blob/master/src/clj/clojure/test.clj#L718 ), so how can I pass a system in parameters of a test ?
(println "Stopping test-system")
(component/stop s)))
(use-fixtures :once test-component-lifecycle)
Note : I am talking about unit-testing here.
I would write a macro, which takes a system-map and starts all components before running tests and stop all components after testing.
For example:
(ns de.hh.new-test
(:require [clojure.test :refer :all]
[com.stuartsierra.component :as component]))
;;; Macro to start and stop component
(defmacro with-started-components [bindings & body]
`(let [~(bindings 0) (component/start ~(bindings 1))]
(try
(let* ~(destructure (vec (drop 2 bindings)))
~#body)
(catch Exception e1#)
(finally
(component/stop ~(bindings 0))))))
;; Test Component
(defprotocol Action
(do-it [self]))
(defrecord TestComponent [state]
component/Lifecycle
(start [self]
(println "====> start")
(assoc self :state (atom state)))
(stop [self]
(println "====> stop"))
Action
(do-it [self]
(println "====> do action")
#(:state self)))
;: TEST
(deftest ^:focused component-test
(with-started-components
[system (component/system-map :test-component (->TestComponent"startup-state"))
test-component (:test-component system)]
(is (= "startup-state" (do-it test-component)))))
Running Test you should see the out put like this
====> start
====> do action
====> stop
Ran 1 tests containing 1 assertions.
0 failures, 0 errors.
I have implemented retry policy for update function (talking to database) - if update throws exception I retry it up to 10 times. I am mocking the update function with midje. I want to simulate that first time it fails and second time it succeeds. I tried this :
(fact "update is retried when failed"
(ud/ensure-data {:username ..username.. :data :h}) => ..result..
(provided
(ud/get-raw-user-data ..username..) => example-user-data-raw
(ud/update-user-data {:username ..username..
:version 1
:userdata {:data {:h {}}}}) =throws=> (Exception.)
(ud/update-user-data {:username ..username..
:version 1
:userdata {:data {:h {}}}}) => ..result..))
but this doesn't seem to work...... Response is :
These calls were not made the right number of times:
(ud/update-user-data {:username ..username.., :version 1, :userdata {:homebases {:h {:sensors []}}}}) [expected at least once, actually never called]
I also found streams (https://github.com/marick/Midje/wiki/Variant-prerequisite-arrows) but I don't know how to combine Exceptions with success calls with streams.
I haven't got a clear understanding of how to use Midje streams either. So my personal solution is, in a word, not to use provided or 'streams', but use with-redefs and 'stubs'.
(defn fn0 [] -1) ;; function we want to mock
;; fn1 retries fn0 if there is an exception
(defn fn1 [] (try
(fn0)
(catch Exception e
(do
(prn "Exception Caught, try again...")
(fn0)))))
(def call-count (atom -1)) ;; counts how many times fn0 is called
;; stub fn0 by returning different result
(defn make-stub [& result-seqs]
(fn [& _]
(swap! call-count inc)
(let [result (nth result-seqs #call-count)]
(if (instance? Throwable result)
(throw result)
result))))
(fact "fn1 ignores first exception and returns second call to fn0"
(with-redefs [fn0 (make-stub (Exception. "stubbed error") 100)]
(fn1) => 100))
I'm trying to write unit tests when using core.async go macros. Writing the test naively, as follows, it appears that the code inside the go blocks don't get executed.
(ns app.core-test
(:require [clojure.test :refer :all]
[clojure.core.async :as async]))
(deftest test1 []
(let [chan (async/chan)]
(async/go
(is (= (async/<! chan) "Hello")))
(async/go
(async/>! chan "Hello"))))
I've managed to get the following working, but it's extremely hacky.
(deftest test1 []
(let [result (async/chan)
chan (async/chan)]
(async/go
(is (= (async/<! chan) "Hello"))
(async/>! result true))
(async/go
(async/>! chan "Hello"))
(async/alts!! [result (async/timeout 10000)])))
Any suggestions as to how I can do this properly?
Tests are executed synchronously, so if you go async the test-runner won't. In Clojure you need to block the test runner via <!!, in ClojureScript you have to return an async test object. This is a generic helper function I use in all my async CLJC tests:
(defn test-async
"Asynchronous test awaiting ch to produce a value or close."
[ch]
#?(:clj
(<!! ch)
:cljs
(async done
(take! ch (fn [_] (done))))))
Your test using it, CLJC compatible and looking way less "hacky":
(deftest test1
(let [ch (chan)]
(go (>! ch "Hello"))
(test-async
(go (is (= "Hello" (<! ch)))))))
It is good practice to assert that the test unblocks, especially during test driven development where you want to avoid locking your test runner. Also, locking is a common cause of failure in async programming, so testing against it is highly reasonable.
To do that I wrote a helper similar to your timeout thing:
(defn test-within
"Asserts that ch does not close or produce a value within ms. Returns a
channel from which the value can be taken."
[ms ch]
(go (let [t (timeout ms)
[v ch] (alts! [ch t])]
(is (not= ch t)
(str "Test should have finished within " ms "ms."))
v)))
You can use it to write your test like:
(deftest test1
(let [ch (chan)]
(go (>! ch "Hello"))
(test-async
(test-within 1000
(go (is (= "Hello" (<! ch)))))))
your test is finishing, and then failing. This happens more reliably if I put a sleep in and then make it fail:
user> (deftest test1 []
(async/<!!
(let [chan (async/chan)]
(async/go
(async/go
(async/<! (async/timeout 1000))
(is (= (async/<! chan) "WRONG")))
(async/go
(async/>! chan "Hello"))))))
#'user/test1
user> (clojure.test/run-tests)
Testing user
Ran 1 tests containing 0 assertions.
0 failures, 0 errors.
{:test 1, :pass 0, :fail 0, :error 0, :type :summary}
user>
FAIL in (test1) (form-init8563497779572341831.clj:5)
expected: (= (async/<! chan) "WRONG")
actual: (not (= "Hello" "WRONG"))
here we can see it report that nothing fails, then it prints the failure message. We can fix this by explicitly coordinating the end of the test and that action finishing by, like most solutions in core.async, adding one more chan.
user> (deftest test1 []
(async/<!!
(let [all-done-chan (async/chan)
chan (async/chan)]
(async/go
(async/go
(async/<! (async/timeout 1000))
(is (= (async/<! chan) "WRONG"))
(async/close! all-done-chan ))
(async/go
(async/>! chan "Hello"))
(async/<! all-done-chan)))))
#'user/test1
user> (clojure.test/run-tests)
Testing user
FAIL in (test1) (form-init8563497779572341831.clj:6)
expected: (= (async/<! chan) "WRONG")
actual: (not (= "Hello" "WRONG"))
Ran 1 tests containing 1 assertions.
1 failures, 0 errors.
{:test 1, :pass 0, :fail 1, :error 0, :type :summary}
Which is equivalent to your solution using alts. I don't think your solution is hackey. With asynchronous code it's always required to pay attention to when things finish, even if you conciously decide to ignore the result.
I'm using an approach similar to Leon's, but with no extra go blocks:
(defn <!!?
"Reads from chan synchronously, waiting for a given maximum of milliseconds.
If the value does not come in during that period, returns :timed-out. If
milliseconds is not given, a default of 1000 is used."
([chan]
(<!!? chan 1000))
([chan milliseconds]
(let [timeout (async/timeout milliseconds)
[value port] (async/alts!! [chan timeout])]
(if (= chan port)
value
:timed-out))))
You can use it simply as:
(is (= 42 (<!!? result-chan)))
Most of the time I just want to read the value from the channel without any extra hassle.