Skip to content

Instantly share code, notes, and snippets.

@naiquevin
Last active January 13, 2018 11:54
Show Gist options
  • Save naiquevin/e37002b7c88e153d4a9c4afe08ffaf81 to your computer and use it in GitHub Desktop.
Save naiquevin/e37002b7c88e153d4a9c4afe08ffaf81 to your computer and use it in GitHub Desktop.
Generative mocking using circleci/bond and core.spec (IN/Clojure 2018 lightning talk)
(ns inclojure-mocks)
;; IN/Clojure 2018 lightning talk :: 13/01/2018
;;
;; Topic: Mocking code (mainly for writing tests)
;;
;; Will be covered:
;;
;; 1. Mocking code using circleci/bond library
;; 2. Generating mocks for code using core.spec and test.check
;;
;; Will *NOT* be covered:
;;
;; 1. What is mocking, why mocking etc.
;; 2. core.spec and test.check
(defn fetch-data
"A fn that fetches data over network"
[x y]
{:conf "IN/Clojure"
:year 2018
:location "Bangalore, India"})
(defn foobar
"Fetches data over network and processes it"
[]
(let [{:keys [year conf location]} (fetch-data 1 2)]
(when year
(if (even? year)
conf
location))))
(comment
(foobar))
;; mocking using bond
(comment
;; https://github.com/circleci/bond
(require '[bond.james :as bond])
(bond/with-spy [fetch-data]
(let [result (foobar)
calls (bond/calls fetch-data)]
(print "Result:")
(prn result)
(print "fetch-data calls:")
(prn calls)
[result calls]))
(def calls (second *1))
(count calls)
(:args(first calls))
(:return (first calls))
(bond/with-stub [fetch-data]
(let [result (foobar)]
(print "Result:")
(prn result)
(print "fetch-data calls:")
(prn (bond/calls fetch-data))))
(bond/with-stub [[fetch-data (constantly {:conf "Clojure Conj"
:year 2017
:location "USA"})]]
(let [result (foobar)]
(print "Result:")
(prn result)
(print "fetch-data calls:")
(prn (bond/calls fetch-data)))))
;; With core.spec and test.check
(comment
;; https://github.com/clojure/core.specs.alpha
;; https://github.com/clojure/test.check
(require '[clojure.spec :as s])
(require '[clojure.spec.gen :as gen])
(s/def ::conf (s/and string? #(re-matches #"^\w+$" %)))
(s/def ::location (s/and string? #(re-matches #"^\w+$" %)))
;; generators for free
(gen/generate (s/gen ::conf))
(gen/generate (s/gen ::location))
;; or your can define a spec with a custom generator
(s/def ::year (s/with-gen number?
#(gen/choose 2010 2018)))
;; composing individual specs into a larger spec
(s/def ::resp-data (s/keys :req-un [::conf ::year ::location]))
(gen/generate (s/gen ::resp-data))
(defn mocker-for-resp-data
[]
(fn [& _]
(let [mock-data (gen/generate (s/gen ::resp-data))]
(println "Mock data")
(prn mock-data)
mock-data)))
(bond/with-stub [[fetch-data (mocker-for-resp-data)]]
(let [result (foobar)]
(print "Result:")
(prn result)
(print "fetch-data calls:")
(prn (bond/calls fetch-data)))))
;; Now we can integrate the above example with core.test, asserting on
;; result and/or bond/calls data
;; Summary:
;;
;; mocking provided by bond + data generation using core.spec = super
;; fast and maintainable tests!
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment