I get it!
Ok, coming from Go (which supports polymorphic interfaces), here's how we always approached mocking:
main
or somewhere close to it).This is all just a traditional approach to the Dependency Inversion Principle. But this approach doesn't really make sense in a functional (ie. stateless) paradigm.
So, suppose you are working with the Quil GUI/animation library in a Clojure project. You'd like to mock out calls to the quil functions for a few reasons:
Reason #2 above is all about being professional, about doing your best to "produce, with each release, a quick, sure, and repeatable proof that every element of the code works as it should." (Item 3, The Programmer's Oath, by Robert Martin.)
So, I've already written about doing basic stubbing with speclj, but this situation is different because of the external quil calls. The answer is to combine the stubbing capabilities of speclj and the (with-redefs)
function:
(ns gui.main-spec
(:require
[quil.core :as q]))
; SUT (system under test)
(defn draw []
(q/fill 42)
#_"remaining code omitted for the sake of brevity")
; Specs
(describe "mocking (external) quil calls"
(with-stubs)
(it "records the provided arguments"
(let [fill-stubbed (stub :fill)]
(with-redefs [q/fill fill-stubbed]
(draw)
(should-have-invoked :fill {:with [42]})))))
So, here's the functional approach:
fill
function. In this case we aren't concerned with a return value (all of the quil drawing functions are invoked for their side-effects).with-redefs
to patch quil's actual fill
function with the stub.with-redefs
.fill
function.Phew! Ok, I'm off to the races again.
An interesting distinction between these two testing approaches is this: in the polymorphic, interface-driven approach, the production code references the abstraction of the interface. In the clojure approach, the production code references the concrete, external function directly and the test sneakily patches it with a fake/stub/mock thing.
-Michael Whatcott