Generate data from a spec but overriding specific fields

I’d like to idiomatically and safely generate data from a spec but overriding specific fields - a bit like plumatic schema’s “complete”.

For example, I have a spec ::customer and I want to generate data (for tests) where :customer/nationality is e.g. specifically :albanian, where the spec for :customer/nationality actually allows #{:albanian :french :british}.

  • I don’t want to generate from ::customer and then assoc :albanian over the top of it.
  • I also don’t want to create a whole new ::albanian-customer spec and generate from that.
  • I do want to use a custom generator as I’d like to follow this comment from the spec guide: “Spec does not trust custom generators and any values they produce will also be checked by their associated spec to guarantee they pass conformance.”

After reading the custom generators guide and googling I’m still missing a good way of doing this - even though I think it should be a relatively common ask. What am I missing?

Any help is much appreciated!

1 Like

As I see it there are two options. s/gen takes an optional overrides map to override any sub-generators with your own. Alternatively, using g/fmap to alter the generated values is an option.

(s/def ::name string?)
(s/def ::age integer?)
(s/def ::person (s/keys :req-un [::name ::age]))

(let [my-generator (s/gen ::person {::age (fn [] (g/return 25))})]
  (g/sample my-generator 3))
;; ({:name "", :age 25} {:name "g", :age 25} {:name "4", :age 25})

(let [my-generator (->> (s/gen ::person)
                        (g/fmap #(assoc % :age 25)))]
  (g/sample my-generator 3))
;; ({:name "", :age 25} {:name "g", :age 25} {:name "4", :age 25})

With the first approach, you get your custom generated values checked against the spec, which is really useful to avoid mistakes (or when the spec changes and you forget to change your custom generators in the tests).

The second approach works but is more brittle IMO.

I’d go with the first one definitely. Also regarding this:

I think you might be misunderstanding the spec guide. It does not say that you shouldn’t use them, it just says that spec will precisely re-check the values you emit from those custom generators to provide the advantages that I outlined about the first method. If anything, it’s a reason to use custom generators instead of fmap or custom assocs.

4 Likes

Thank you! Spec gen taking an overrides map was definitely what I was missing :slight_smile: I think misread my bullet with the quote though… I’d said I DO want to use a custom generator, for exactly the reasons you stated and I quoted!

1 Like

Have you heard about specmonstah? Seems to be aimed at solving the exact same problem you are describing.

1 Like

Here’s the precise part of the readme of Specmonstah that allows for overrides https://github.com/reifyhealth/specmonstah/blob/develop/README.md#07-spec-gen-customization-and-omission

This topic was automatically closed 182 days after the last reply. New replies are no longer allowed.