unable to remove duplication when composing tests - clojure

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

How do you use an existing vector of predicates with :post conditions in Clojure?

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.

In Clojure, how to unit test an async Pedestal interceptor?

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.

Test clojure instance in deftest

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.

Reusing the same let bindings

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"))

Threading arrow private defns in clojure.test

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