Using Simple Check With Expectations

Update (1/19/14): Jay Field’s has posted an example of how to get better failure handling when using simple-check with expectations by using a CustomPred: https://github.com/jaycfields/simple-expectations


I recently wanted to try out check out Reid Draper’s simple-check in a side project of mine. Simple-Check is a property-based testing library inspired by Haskell’s famous QuickCheck. When using simple-check, rather than testing against your functions against hand-written sample inputs and outputs, you specify properties that should hold true for your function, and let simple-check randomly generate inputs to the function. See the readme for some concrete examples.

I’m currently using Expectations for my unit testing, a simple and minimalist testing framework. There’s no built in support for simple-check, and simple-check doesn’t provide any helpers for Expectations the way it does for clojure.test, but it’s easy to get them to play nicely together. Simple-Check returns a map containing a :result key which will have the value true or false depending on whether or not the test passed. To use expect with simple-check, all we need to do is check that :result contains true:

;; sorting is idempotent
(expect #(:result %)
        (sc/quick-check 100
          (prop/for-all [v (gen/vector gen/int)]
            (= (sort v) (sort (sort v)))))

This works, but will result in a lot of duplicated expectations and calls to quick-check.

Simple-Check provides a nice macro called defspec to eliminate this kind of reptition when working with clojure.test, but nothing for Expectations. Luckily, it’s pretty trivial to create our own:

(defmacro expect-prop [n prop-fn]
  `(expect #(:result %)
     (sc/quick-check ~n ~prop-fn)))

;; sorting is idempotent
(expect-prop 100 
             (prop/for-all [v (gen/vector gen/int)]
               (= (sort v) (sort (sort v)))))

I didn’t show ns form for brevity above, the full namespace is here if you’re interested:

(ns simple-check-test.core
 (:require [expectations :refer :all]
           [simple-check.core :as sc]
           [simple-check.generators :as gen]
           [simple-check.properties :as prop]))

;; sorting is idempotent 
(expect #(:result %) 
        (sc/quick-check 100 
          (prop/for-all [v (gen/vector gen/int)]
            (= (sort v) (sort (sort v)))))

(defmacro expect-prop [n prop-fn]
    `(expect #(:result %)
       (sc/quick-check ~n ~prop-fn)))

;; sorting is idempotent with our newly created macro
(expect-prop 100 
             (prop/for-all [v (gen/vector gen/int)]
               (= (sort v) (sort (sort v)))))