Specifications for function definitions
A function specification can contain a specification for the arguments, the return values and the relationship between the two.
The specifications for the function may be composed from previously defined data specifications.
Function definitions
The card game application has three functions to start with.
(defn regulation-card-deck
"Generate a complete deck of playing cards"
[{:keys [::deck ::players] :as game}]
(apply + (count deck)
(map #(-> % ::delt-hand count) players)))
At the start of function design, the algorithm may still be undefined. Using the specifications and generators mock data can be returned as a placeholder.
(defn deal-cards
"Deal cards to each of the players
Returns updated game hash-map"
[game]
(spec-gen/generate (spec/gen ::game)))
(defn winning-player
"Calculate winning hand by comparing each players hand
Return winning player"
[players]
(spec-gen/generate (spec/gen ::player)))
Function specification
(spec/fdef deal-cards
:args (spec/cat :game ::game)
:ret ::game
:fn #(= (regulation-card-deck (-> % :args :game))
(regulation-card-deck (-> % :ret))))
(spec/fdef winning-player
:args (spec/cat :players ::players)
:ret ::player)
Instrument functions
Instrumenting functions will wrap a function definition and check the arguments of any call to the instrumented function.
(spec-test/instrument `deal-cards)
Ignore the Warning message in the result, this is an issue with the REPL implementation used in this page.
Calling the deal-cards
function with an incorrect argument returns an error that describes where in the specification the error occurred.
(deal-cards "fake game data")
Error in an easier to read format
ERROR: #error
{:message "Call to #'practicalli.card-game/deal-cards did not conform to spec:\n\
"fake game data\" - failed:
map? in: [0] at: [:args :game] spec: :practicalli.card-game/game\n",
:data {:cljs.spec.alpha/problems
[{:path [:args :game],
:pred cljs.core/map?,
:val "fake game data",
:via [:practicalli.card-game/game :practicalli.card-game/game],
:in [0]}],
:cljs.spec.alpha/spec #object[cljs.spec.alpha.t_cljs$spec$alpha17968],
:cljs.spec.alpha/value ("fake game data"),
:cljs.spec.alpha/args ("fake game data"),
:cljs.spec.alpha/failure :instrument}}