I am unable to remove duplication inside a clojure.test test.
Suppose I have multiple implementations for the same abstraction:
(defn foo1 [] ,,,)
(defn foo2 [] ,,,)
(defn foo3 [] ,,,)
and I also have a test that all implementations should pass:
(defn test-impl [foo]
(is (= ,,, (foo))))
I can now create a clojure.test test that checks all the implementations in a single step:
(deftest test-all-impls
(test-impl foo1)
(test-impl foo2)
(test-impl foo3))
All is good; running the tests in the REPL I get:
(run-tests)
Testing user
Ran 1 tests containing 3 assertions.
0 failures, 0 errors.
=> {:test 1, :pass 3, :fail 0, :error 0, :type :summary}
I now want to modify test-all-impls to remove the duplication of having to invoke test-impl explicitly for each of the implementations. It occurs to me to modify test-all-impls as follows:
(deftest test-all-impls
(for [foo [foo1 foo2 foo3]] (test-impl foo))
Hmm, now not all is good; in the REPL I get:
(run-tests)
Testing user
Ran 1 tests containing 0 assertions.
0 failures, 0 errors.
=> {:test 1, :pass 0, :fail 0, :error 0, :type :summary}
What am I missing?
To get around the lazyness of for, use doseq instead:
(deftest test-all-impls
(doseq [foo [foo1 foo2 foo3]] (test-impl foo))
Another answer is to convert the result into a vector, which will force the for loop to run:
(deftest test-all-impls
(vec (for [foo [foo1 foo2 foo3]] (test-impl foo))))
Related
Given that :post takes a form that gets evaluated later (e.g. {:post [(= 10 %)]}). How could one dynamically pass a 'pre-made' vector of functions to :post?
For example:
(def my-post-validator
[prediate1 predicate2 predicate3])
(defn foo [x]
{:post my-post-validator}
x)
this throws a syntax error
Don't know how to create ISeq from: clojure.lang.Symbol
With my fuzzy understanding, it's because defn is a macro, and the thing that allows the % syntax in :post is that it's quoted internally..?
I thought maybe I then use a macro to pass a 'literal' of what I wanted evaluated
(defmacro my-post-cond [spec]
'[(assert spec %) (predicate2 %) (predicate n)])
example:
(defn foo [x]
{:post (my-post-cond :what/ever)}
x)
However, this attempt gives the error:
Can't take value of a macro
Is there a way to pass a vector of things to :post rather than having to define it inline?
You can't pass a vector of predefined predicates, but you can combine multiple predicates under a single name and use that name in :post:
(defn my-post-cond [spec val]
(and
;; Not sure if this is exactly what you want,
;; given that `val` becomes an assert message.
(assert spec val)
(predicate2 val)
;; You used `n` - I assume it was supposed to be `%`.
(predicate val)))
(defn foo [x]
{:post [(my-post-cond :what/ever %)]}
x)
I started off as a fan of pre- and post-conditions, but I've changed over the years.
For simple things, I prefer to use Plumatic Schema to not only test inputs & outputs, but to document them as well.
For more complicated tests & verifications, I just put in an explicit assert or similar. I also wrote a helper function in the Tupelo library to reduce repetition, etc when debugging or verifying return values:
(ns tst.demo.core
(:use tupelo.core tupelo.test))
(defn oddly
"Transforms its input. Throws if result is not odd"
[x]
(let [answer (-> x (* 3) (+ 2))]
(with-result answer
(newline)
(println :given x)
(assert (odd? answer))
(println :returning answer))))
(dotest
(is= 5 (oddly 1))
(throws? (oddly 2)))
with result
------------------------------------
Clojure 1.10.3 Java 11.0.11
------------------------------------
Testing tst.demo.core
:given 1
:returning 5
:given 2
Ran 2 tests containing 2 assertions.
0 failures, 0 errors.
Passed all tests
So with either the println or assert, the returned value is easy to see. If it fails the assert, an Exception is thrown as normal.
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.
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.
I'm refactoring my test suite for a clojure application and am trying to figure out if there is a way to save let bindings for reuse to minimize copied code, as many tests require the similar setup but interfere with each other and require their own deftest. Ideally, I would like it to work a little like this:
(def let-bindings [bind1 val1 bind2 val2 bind3 val3])
(deftest test-1
(testing "my test"
(let (conj let-bindings [bind4 val4])
...)))
(deftest test-2
(testing "my second test"
(let (conj let-bindings [bind5 val5])
...)))
For added clarification, I need val1 and val2 to be evaluated in the test, not when defining the let bindings, as the calls affect the test database in a way that needs to be reset after each test. Does this mean I need a macro?
let is a special form so trying to metaprogram it without doing it in a macro won't work. Perhaps you should just define the common bindings instead:
(def bind1 val1)
(def bind2 val2)
(def bind3 val3)
(deftest test-1
(testing "my test"
(let [bind4 val4]
;; here bind1...bind4 would be available
...)))
...
EDIT
Here is how I imagine you could do it with a macro:
;; notice that the let will be recreated each time so if
;; val1 ... are computational hard consider caching.
(defmacro elet [ bindings & body ]
`(let [bind1 val1 bind2 val2 bind3 val3 ~#bindings]
~#body))
(deftest test-1
(testing "my test"
(elet [bind4 val4]
;; here bind1...bind4 would be available
...)))
...
To do this without macros you could go this way:
(def m {:a 1 :b 2})
(deftest foo []
(println (m :a)))
(foo)
;; prints 1
(let [m (assoc m :c 3)]
(deftest bar
(println (m :c))))
(bar)
;; prints 3
You can use fixtures to redefine dynamic bindings across tests and access the values within deftest. Fixtures can be defined once for all or each deftest.
(def ^:dynamic m)
(defn once-fixture
[tests]
(binding [m {:a 1 :b 2}]
(tests)))
(use-fixtures :once once-fixture)
(deftest testing-binding
(is (= (:a m) 1)
"Dynamic binding is working"))
Consider the following functions in an MVE (minimal viable example) namespace from a fresh lein new app arrow-mve. The function extract-one is public, and the function extract-two is private. I've included the main- function just for completeness and for the remote possibility that it's entailed in my problem:
(ns arrow-mve.core
(:gen-class))
(defn extract-one [m]
(-> m :a))
(defn- extract-two [m]
(-> m :a))
(defn -main
"I don't do a whole lot ... yet."
[& args]
(println "Hello, World!"))
In my parallel test namespace, I can test these functions as follows. I can test the public function extract-one either by a direct call or using the arrow-threading macro ->. Also notice that I have no problem referring to the private function, extract-two, by its full Var in a direct call. These tests pass:
(ns arrow-mve.core-test
(:require [clojure.test :refer :all]
[arrow-mve.core :refer :all]))
(deftest test-one-a
(is (= 1 (extract-one {:a 1, :b 2}))))
(deftest test-one-b
(is (= 1 (-> {:a 1, :b 2}
extract-one))))
(deftest test-two-a
(is (= 1 (#'arrow-mve.core/extract-two
{:a 1, :b 2}))))
But I get a compile error when I attempt to call the private function extract-two with the arrow macro:
(deftest test-two-b
(is (= 1 (-> {:a 1, :b 2}
#'arrow-mve.core/extract-two))))
$ lein test
Exception in thread "main" java.lang.RuntimeException: Unable to resolve
var: arrow.mve.core/extract-two in this context, compiling:
(arrow_mve/core_test.clj:10:12)
at clojure.lang.Compiler.analyzeSeq(Compiler.java:6875)
at clojure.lang.Compiler.analyze(Compiler.java:6669)
at clojure.lang.Compiler.analyze(Compiler.java:6625)
Things get more strange when I make the test a little more complex.
(deftest test-two-b
(is (= {:x 3.14, :y 2.72}
(-> {:a {:x 3.14, :y 2.72}, :b 2}
#'arrow-mve.core/extract-two))))
$ lein test
Exception in thread "main" java.lang.ClassCastException:
clojure.lang.PersistentArrayMap cannot be cast to clojure.lang.Symbol,
compiling:(arrow_mve/core_test.clj:18:10)
at clojure.lang.Compiler.analyzeSeq(Compiler.java:6875)
at clojure.lang.Compiler.analyze(Compiler.java:6669)
at clojure.lang.Compiler.analyzeSeq(Compiler.java:6856)
Again, the test passes in the direct-call form:
(deftest test-two-b
(is (= {:x 3.14, :y 2.72}
(#'arrow-mve.core/extract-two
{:a {:x 3.14, :y 2.72}, :b 2}))))
I suspect that the problem is a limitation of macro-chaining through deftest, is, the reader macro #' for Var, and the arrow macro, and wondered if it was by design or a potential bug. Of course, in my real application (not this MVE), I have long and deep call chains that make using the arrow macros highly desirable.
Here is the answer (different ns):
Main namespace:
(ns clj.core
(:require [tupelo.core :as t] ))
(t/refer-tupelo)
(defn extract-one [m]
(-> m :a))
(defn- extract-two [m]
(-> m :a))
Testing namespace:
(ns tst.clj.core
(:use clj.core
clojure.test )
(:require [tupelo.core :as t]))
(t/refer-tupelo)
(deftest test-one-a
(is (= 1 (extract-one {:a 1, :b 2}))))
(deftest test-one-b
(is (= 1 (-> {:a 1, :b 2}
extract-one))))
(deftest test-two-a1
(is (= 1 (#'clj.core/extract-two {:a 1, :b 2}))))
;(deftest test-two-b
; (is (= 1 (-> {:a 1, :b 2}
; clj.core/extract-two)))) ; fails: not public
;(deftest test-two-b1
; (is (= 1 (-> {:a 1, :b 2}
; #'clj.core/extract-two))))
; fails: can't cast PersistentArrayMap to Symbol
(deftest test-two-b
(is (= 1 (-> {:a 1, :b 2}
(#'clj.core/extract-two))))) ; works
The answer is that the var reference needs to be inside parentheses. The thread macros all have a test of the form (pseudocode):
(if (not (list? form))
'(form)
form)
So a form like
(-> 1
inc)
is transformed into
(-> 1
(inc))
before the rest of the threading occurs. The if test seems to be failing for you since the var is not a symbol. Enclosing the var in a list as a function call fixes the problem.
I prefer to always enclose the function calls in threading forms in parentheses, and not use any "naked" functions even though it is normally allowable:
(-> 1
(inc) ; could have typed "inc" w/o parens
(* 2)) ; must use parens since more than 1 arg
;=> 4