This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2018-08-23
Channels
- # announcements (2)
- # beginners (246)
- # boot-dev (1)
- # braveandtrue (3)
- # calva (13)
- # cider (26)
- # cljs-dev (6)
- # clojure (75)
- # clojure-finland (4)
- # clojure-germany (39)
- # clojure-italy (1)
- # clojure-mexico (1)
- # clojure-nl (14)
- # clojure-spec (61)
- # clojure-uk (104)
- # clojurescript (125)
- # cursive (20)
- # datomic (1)
- # emacs (2)
- # figwheel-main (91)
- # fulcro (29)
- # graphql (9)
- # jobs (3)
- # jobs-discuss (9)
- # juxt (13)
- # liberator (2)
- # luminus (1)
- # off-topic (15)
- # parinfer (8)
- # re-frame (70)
- # reagent (35)
- # reitit (24)
- # remote-jobs (5)
- # ring-swagger (3)
- # shadow-cljs (127)
- # spacemacs (34)
- # yada (6)
i'm struggling a bit on how to design my spec rules. specifically, i have a complex object that i want to spec, and this object can have several traits, depending upon where it is.
so, for example, let's say i have an animal
object with the following definition:
(s/def ::vaccinations (s/coll-of {:foo :bar})
(s/def ::medical (s/keys :opt-un [::vaccinations])
(s/def ::animal (s/keys :req-un [::medical]))
so here we can see that an animal has a optional medical information about vaccinations. most of my functions just work with animal
s, and do not care whether some specific nested property it true. however, some other functions do care whether an animal is vaccinated. for now, i have this:
(s/def ::vaccinated-animal (s/and ::animal #(some? (get-in % [:medical :vaccinations])))
but this feels a bit hacky, and doesn't scale very well when you want to compose multiple properties into it. it also feels like i'm developing a poor man's trait system here, which is maybe the intention, maybe not
regardless, what would be the proper way to organize this stuff be?would i s/merge
the ::animal into an ::vaccinated-animal
? but how would i re-define a property of a nested map in this case (i.e., :req-un
the ::vaccinations
rather than :opt-un
) ?
So basically
(s/def ::medical (s/keys :opt-un [::vaccinations])
or
(s/def ::medical (s/keys :req-un [::vaccinations])
I liked the use of that from http://conan.is/blogging/clojure-spec-tips.html
(s/def :company.department.employee/name string?)
(s/def :company.department.employee/payroll-number nat-int?)
(s/def :company.department/employee
(s/keys :req [:company.department.employee/name
:company.department.employee/payroll-number]))
(s/def :company.department/employees (s/coll-of :company.department/employee))
(s/def :company.department/id uuid?)
(s/def :company/department (s/keys :req [:company.department/id
:company.department/employees]))
The reason i think the above is useful to you is it allows you to use ns’s to sorta describe what you are talking about
It depends on what you want to test, but on strategy for building generators like this is to some from a small set of valid keys OR random keys
Is the goal of the generator to produce refs that will always resolve, or just be valid syntax (but maybe not resolve), or or a mix of resolved and unresolved refs?
i.e. do you want all paths to be valid (assuming “valid” means “it resolves to some actual path in the map”) or is it OK if some are invalid?
That’s true, you can’t spec an exception occurs, although you could generate EDN with a mix and then use a normal generative test (i.e. not using check
directly) to say either an exception should occur or some other thing should happen
Anyway, if you want a mix, one thing you could try is to normally name keys from a small set in the generator e.g. {:a :b :c}
and then generate refs of a usually small length from those same keys e.g. (s/coll-of #{:a :b :c} :kind vector?)
. Basically you’re just assuming you’ll get some number of “collisions” but accepting some refs won’t resolve
If you want to ensure every path is going to resolve, then I think you’ll have to build a custom generator for the entire config. I’d start by generating a set of paths that will be included in the map. Then, you can take those valid paths and write a function that fills in the values for each path, which may contain a ref to another valid path
Super hacky (and there are bugs), but here’s a quick outline of how a generator might work. Hope it helps! 😄 https://gist.github.com/bhb/b4ca38670c9c20f3d2ed8623aca5ec11