Organizing Specificationsλ︎
Data and function definition specifications are typically placed in a dedicated specification namespaces, e.g src/domain/project/specification.clj.
Add the data specifications (spec/def), custom predicate functions and function specifications (spec/fdef) to the specifications namespace.
Specifications for an architecture layer can be organised next to the namespaces managing the layer, e.g. database.
Migrate specifications to a library once they are applicable to multiple projects.
Instrumenting functionsλ︎
Add spec-test/instrument expressions to the specifications file, after the spec/fdef expressions.
Rather than create individual expressions, create a clojure.core/def to contain a collection of all the spec/fdef expressions. This list can then be used to instrument and unstrument all the spec/fdef specifications.
Write simple helper functions to wrap the instrumenting of function specifications
(defn instrument-all-functions
[]
(spec-test/instrument function-specifications))
(defn unstrument-all-functions
[]
(spec-test/unstrument function-specifications))
Unit testingλ︎
Specifications can be incorporated into the existing unit tests, so it is sensible to keep them under the corresponding test directory files.
Generative testingλ︎
Using spec-test/check will generate 1000 data values for each expression, so by default these tests will take far longer that other tests.
Configuring generative tests to only generate a small number of values will make spec-test/check expressions return almost instantaneously. In this example, only 10 data values are generated
Generative testing with small generators can be run regularly during development without impacting fast feedback.