fusion
fusion

Reputation: 1287

Testing collection items using deftest

I'm trying to write some tests using clojure.test and the deftest macro.

(deftest a-test
  (for [item collection]
    (is (= (f item) :a-value))))

but I've realized that this is not the proper way to execute this test. I've searched the documentation and found the 'are' macro but I'm not sure how to use it in these circumstances.

What is the preferred way to write such a test?

Upvotes: 3

Views: 620

Answers (2)

trptcolin
trptcolin

Reputation: 2340

It's hard to say what you should use without knowing more specifics about the problem you're trying to solve, but I can give you some possibilities that may be useful, depending on what the actual underlying problem is.

If (as I suspect) you're having trouble with the laziness of for, a simple change to ensure laziness doesn't bite you (it may be OK already) would be to change for to doseq.

For example,

(deftest b-test (for [item (range 100)] (test/is (< item 80))))

will pass due to the laziness of the sequence generated by for, which is not a loop but a list comprehension. This is probably not what I intended. But

(deftest c-test (doseq [item (range 100)] (test/is (< item 80))))

will have a bunch of failures (for every item greater than or equal to 80). Note that the only difference is for -> doseq.

are is about providing different inputs & outputs that pass a given predicate, so assuming :a-value can vary across applications of f, that may be useful:

(deftest a-test
  (are [x y] (= x y)
     (f item-1) :value-1
     (f item-2) :value-2
     (f item-3) :value-3))

If, on the other hand, each member of collection should have the same result when f is applied, it's more concise to say something like this:

(deftest a-test
  (is (every? (fn [item] (= (f item) :a-value))
              collection)))

Upvotes: 1

Alex
Alex

Reputation: 13961

The reason your test doesn't work is that for simply builds a lazy sequence; it is not an imperative loop as it is in other programming languages. There are several ways you could rewrite this test.

Use the every? function to test that all items in a collection satisfy a predicate. This is probably the least amount of code, but has the disadvantage that if the test fails, you won't know which particular item failed.

(deftest a-test
  (is (every? #(= (f %) :a-value) collection)))

Rewrite the test using are. This is a good option, and slightly more idiomatic, if the items in the collection being tested are a hard-coded list of inputs specific to this test.

(deftest a-test
  (are [item] (= (f item) :a-value)
    :a
    :b
    :c
    ;; other collection items here...
  ))

Finally, you could swap out the for with a doseq. This is slightly less idiomatic for writing tests, but has the advantage that it will work with a collection of test inputs that is generated programmatically.

(deftest a-test
  (doseq [item collection]
    (is (= (f item) :a-value))))

Upvotes: 9

Related Questions