Fork me on GitHub
#clojure-spec
<
2022-03-15
>
Mark Wardle21:03:59

Hello all. Using clojure.spec a lot now, and also using for generative testing. Working really well, but in one domain I need to generate 'unique' [obv only in that test run] integer identifiers. Unfortunately, they also need to match a specific pattern and have a Verhoeff check digit. My spec and a custom generator is: (defn gen-identifier "A generator of identifiers of the specified type. Parameters: - t : one of :info.snomed/Concept :info.snomed.Description or :info.snomed/Relationship." [t] (gen/fmap #(let [partition (rand-nth (seq (partitions-for-type t)))] (Long/parseLong (verhoeff/append (str % partition)))) (s/gen (s/int-in 100000 Long/MAX_VALUE)))) https://github.com/wardle/hermes/blob/46cfee5b8005ffe8e26d86dcbbb239ba7b8ef01a/src/com/eldrix/hermes/rf2spec.clj#L18 I can do this for some by simply filtering out duplicates if I'm generating a single batch, but there are other times when that is not possible. I guess I could fallback to an incrementing counter and forego using a generator, or map through generated entities and override with an autoincrementing integer generator from an atom, but is there something in spec I'm missing? I've seen the list-distinct-by but that appears to offer guarantees only within the list obviously. I was thinking about something that starts with something based on time but then increments to satisfy the other generative predicates. Then it starts to feel a bit tricky! Anyone else faced this issue, or have any suggestions? I am missing something obvious? All suggestions welcomed. Thank you

colinkahn22:03:13

When using test.check I’ve generated these using an atom as a counter in the past. Then it’s a matter of using gen/fmap where it’s generator is all the different things that should be related, and the transform function assigns the ids to be consistent. The generator looks like this:

(def counter (atom 0))

(def gen-unique
  (gen/no-shrink (gen/fmap (fn [_] (str (swap! counter inc))) (gen/return nil))))
Interested though if other people have different ways to do this. I don’t know if it’s a downside, but it’s extra coordination at the gen/fmap to “reconcile” the parts (but I’ve found this necessary at some level for almost all generators i’ve written).

Mark Wardle22:03:31

Oh that looks really good thank you. I shall give that a try. Hadn't used gen/no-shrink either before! Thanks again.