Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

how to unit test for laziness

I have a function that is supposed to take a lazy seq and return an unrealized lazy seq. Now I want to write a unit test (in test-is btw) to make sure that the result is an unrealized lazy sequence.

like image 309
Arthur Ulfeldt Avatar asked Oct 30 '09 19:10

Arthur Ulfeldt


2 Answers

user=> (instance? clojure.lang.LazySeq (map + [1 2 3 4] [1 2 3 4]))
true

If you have a lot of things to test, maybe this would simplify it:

(defmacro is-lazy? [x] `(is (instance? clojure.lang.LazySeq ~x)))

user=> (is-lazy? 1)

FAIL in clojure.lang.PersistentList$EmptyList@1 (NO_SOURCE_FILE:7)
expected: (clojure.core/instance? clojure.lang.LazySeq 1)
  actual: (not (clojure.core/instance? clojure.lang.LazySeq 1))
false
user=> (is-lazy? (map + [1 2 3 4] [1 2 3 4]))
true

As of Clojure 1.3 there is also the realized? function: "Returns true if a value has been produced for a promise, delay, future or lazy sequence."

like image 53
Timothy Pratley Avatar answered Oct 12 '22 23:10

Timothy Pratley


Use a function with a side effect (say, writing to a ref) as the sequence generator function in your test case. If the side effect never happens, it means the sequence remains unrealized... as soon as the sequence is realized, the function will be called.

First, set it up like this:

(def effect-count (ref 0))

(defn test-fn [x]
    (do
        (dosync (alter effect-count inc))
        x))

Then, run your function. I'll just use map, here:

(def result (map test-fn (range 1 10)))

Test if test-fn ever ran:

(if (= 0 @effect-count) 
    (println "Test passed!")
    (println "Test failed!"))

Since we know map is lazy, it should always work at this point. Now, force evaluation of the sequence:

(dorun result)

And check the value of effect-count again. This time, we DO expect the side effect to have triggered. And, it is so...

user=>@effect-count
9
like image 33
levand Avatar answered Oct 13 '22 00:10

levand