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.
Related
Example adapted from the standard documentation for with-test:
(ns my-test
(:require [clojure.test :refer :all]))
(with-test
(defn my-function [x y]
;; (assert false)
(+ x y))
(is (= 4 (my-function 2 2)))
(is (= 7 (my-function 3 4))))
(test #'my-function)
The 'with-test' block and the 'test' blocks are evaluated correctly (using C-x C-e in emacs/cider).
But then if I uncomment the 'assert' line they still are!
Directly evaluating (my-function 2 2) fails.
When I start a repl with lein repl I can run the function greet and it works as expected.
(ns var-test.core
(:gen-class))
(declare ^:dynamic x)
(defn greet []
(binding [x "Hello World."]
(println (load-string "x"))))
(defn -main [& args]
(greet))
But if run the code via lein run it fails with
java.lang.RuntimeException: Unable to resolve symbol: x in this context.
What am I missing?
Is the var x dropped during compilation, despite being declared, since it is never used outside of the string?
Edit:
Solution
#amalloy's comment helped me understand I need to bind *ns* in order load the string within the expected namespace, instead of a new, empty namespace.
This works as expected:
(ns var-test.core
(:gen-class))
(declare ^:dynamic x)
(defn greet []
(binding [x "Hello World."
*ns* (find-ns 'var-test.core)]
(println (load-string "x"))))
(defn -main [& args]
(greet))
Wow, I've never seen that function before!
According to the docs, load-string is meant to read & load forms one-at-a-time from an input string. Observe this code, made from my favorite template project:
(ns tst.demo.core
(:use tupelo.core tupelo.test)
(:require [tupelo.string :as str]))
(dotest
(def y "wilma")
(throws? (eval (quote y)))
(throws? (load-string "y"))
So it appears that load-string starts with a new, empty environment, then reads and evaluates forms one at a time in that new env. Since your x is not in that new environment, it can't be found and you get an error.
Try it another way:
(load-string
(str/quotes->double
"(def ^:dynamic x)
(binding [x 'fred']
(println :bb (load-string 'x'))) " ))
;=> :bb fred
In this case, we give all the code as text to load-string. It reads and eval's first the def, then the binding & nested load-string forms. Everything works as expected since the working environment contains the Var for x.
Some more code illustrates this:
(spy :cc
(load-string
"(def x 5)
x "))
with result
:cc => 5
So the eval produces the var x with value 5, then the reference to x causes the value 5 to be produced.
To my surprise, the partial load-string works in a fresh REPL:
demo.core=> (def x "fred")
#'demo.core/x
demo.core=> (load-string "x")
"fred"
So load-string must be coded to use any pre-existing
REPL environment as the base environment. When using lein run, there is no REPL environment available, so load-string starts with an empty environment.
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.
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)))))
Found some odd behavior in midje, not sure if it's midje related, or due to my misunderstanding of some clojure constructs, but it's puzzling:
Inside a facts statement, a for loop is not getting called:
(ns t1
(:require [midje.sweet :refer :all ] )
)
(facts
(println "ok") ; -- this prints fine
(for [val '(1 2 3)] (println val)) ; this does not
(fact "junk"
(> (.length "aaaaha") 3) => true ))
Thought maybe it had something to do with the for being overwritten in the ns but calling clojure.core/for behaves similarly.
clojure.core/for "...yields a lazy sequence..."
You need to realize the sequence to see its side effects.
(doall (for [val '(1 2 3)] (println val)))
I'd suggest using something more appropriate like clojure.core/doseq:
(doseq [val '(1 2 3)] (println val))