Using generating test library in clojure vs build your own using higher order functions

Clojure has several libraries for generative testing such as test.check , test.generative or data.generators .

Higher-order functions can be used to create random data generators, which can be as follows:

(defn gen [create-fn content-fn lazy]
  (fn [] (reduce #(create-fn %1 %2) (for [a lazy] (content-fn)))))

(def a (gen str #(rand-nth [\a \b \c]) (range 10)))

(def b (gen vector #(rand-int 10) (range 2)))

(def c (gen hash-set b (range (rand-int 10))))


This is just an example and can be modified with various parameters, filters, particles, etc. to create data generation functions that are quite flexible.

Is there something that any of the generator libraries can do that is not so easy (or more) achievable by composing some higher order functions?

As a side note to the gods of stackoverflow: I don't believe this question is subjective. I am not asking for an opinion on which library is better. I want to know what feature (s) or technique (s) of any / all generator data libraries makes them different from composing higher order functions of vanilla. The sample answer should illustrate the generation of random data using any of the libraries, with an explanation of why it would be more difficult to do this by composing the HOF in the way I illustrated above.


source to share

1 answer

test.check makes this way better. First of all, suppose you are creating a random list of 100 elements and your test fails: something about how you handled this list is wrong. Now what? How do you find the root error? It does not, of course, depend on these 100 inputs; you could reproduce it with just a few elements, or even an empty list if there is something wrong with your base case.

The feature that makes this all really useful is not the random generators, but the shrinkage of those generators. After test.check finds an input that breaks your tests, it tries to make the input as simple as possible while making your tests break. For a list of integers, the abbreviations are simple enough that you can make them yourself: remove any element or shrink any element. Even that might not be true: choosing the order to compress is probably a more complex issue than I understand. And for large inputs like a list of maps from vectors to 3-tuples [string, int, keyword], you will find it completely unmanageable, while test.check has already done all the hard work.



All Articles