mockfn
is a library supporting mockist
test-driven-development in Clojure. It is meant to be used alongside a regular
testing framework such as clojure.test
.
In order to use mockfn
, it's enough to require it in a test namespace.
(:require [mockfn.macros :as mfn]
[mockfn.matchers :as matchers]
...)
This will bring mockfn
features in scope for this namespace.
The providing
macro replaces functions with mocks. These mocks return
preconfigured values when called with the expected arguments.
(testing "providing"
(providing [(one-fn) :result]
(is (= :result (one-fn)))))
As presented below, a mock (one-fn
) can be configured with different returns
for different arguments.
(testing "providing - one function, different arguments"
(providing [(one-fn :argument-1) :result-1
(one-fn :argument-2) :result-2]
(is (= :result-1 (one-fn :argument-1)))
(is (= :result-2 (one-fn :argument-2)))))
It's also possible to configure multiple mocks, for multiple functions, at once.
(testing "providing with more than one function"
(providing [(one-fn :argument) :result-1
(other-fn :argument) :result-2]
(is (= :result-1 (one-fn :argument)))
(is (= :result-2 (other-fn :argument))))))
calling
If you would like to call the value instead of returning it, use mockfn.macros/calling
:
(testing "providing - calling mocked value as a function"
(providing [(one-fn) (calling #(throw (ex-info "one-fn failed" {:args 'none})))]
(is (thrown? ExceptionInfo (one-fn)))))
unmocked
When mocking it is sometimes useful to set some mocks to point to their
original implementation. This can be done by using mockfn.macros/unmocked
:
(testing "providing - using unmocked to default to original function"
(providing [(one-fn :argument-1) unmocked
(one-fn :argument-2) :result-2]
(is (thrown? ExceptionInfo (one-fn)))))
Private functions can be mocked by following Clojure's convention of referring
them by their var, prefixing them with #'
.
(testing "providing - private function"
(providing [(#'private-fn) :result]
(is (= :result (#'private-fn)))))
The verifying
macro works similarly, but also defines an expectation for the
number of times a call should be performed during the test. A test will fail if
this expectation is not met.
(testing "verifying"
(verifying [(one-fn :argument) :result (exactly 1)]
(is (= :result (one-fn :argument)))))
Notice that the expected number of calls is defined with a matcher.
Mocks can be configured to return a specific value for a range of different arguments through matchers.
(testing "argument matchers"
(providing [(one-fn (at-least 10) (at-most 20)) 15]
(is (= 15 (one-fn 12 18))))))
Support for clojure.test
is provided in the mockfn.clj-test
namespace.
(:require [clojure.test :refer :all]
[mockfn.clj-test :as mfn]
[mockfn.matchers :as matchers]
...)
The mockfn.clj-test/deftest
and mockfn.clj-test/testing
macros replace
clojure.test/deftest
and clojure.test/testing
and support a flatter (as in
not nested) mocking style using mockfn.clj-test/providing
and
mockfn.clj-test/verifying
:
(mfn/deftest deftest-with-builtin-mocking
(is (= :one-fn (one-fn)))
(mfn/providing
(one-fn) :one-fn)
(mfn/testing "testing with built-in-mocking"
(is (= :one-fn (one-fn)))
(is (= :other-fn (other-fn)))
(mfn/verifying
(other-fn) :other-fn (exactly 1))))
Notice that in order to leverage the built-in support for mocking in these
macros, it's necessary to use the providing
and verifying
versions provided
at the mockfn.clj-test
namespace.
The following matchers are included in mockfn
:
(exactly expected)
Matches if actual value is equal to the expected value.
(at-least expected)
Matches if actual value is greater or equal than the expected value.
(at-most expected)
Matches if actual value is less or equal than the expected value.
(any)
Always matches.
(a expected)
Matches if actual value is an instance of the expected type.
(pred odd?)
Matches when the predicate applied to the actual value results in a truthy value.
Functions will automatically be interpreted as pred
matchers, and hence don't
need to explicitly be wrapped in the pred
matcher.
Functions placed in the argument position of mocks will be treated as pred
matchers by default. Thus, one can make use of external predicate functions to
perform matching.
matcher-combinators
is a
library for comparing nested datastructures and pretty printing mismatch diffs.
It can be used to specify mockfn
argument matching behavior, for example:
(require '[matcher-combinators.standalone :refer [match?]]
'[matcher-combinators.matchers :as m])
(testing "matcher-combinator example"
(providing [(one-fn (match? (m/in-any-order [1 3 2]))) :result-unordered]
(is (= :result-unordered (one-fn [3 2 1])))))
While providing
and verifying
calls can be nested, all required stubs and
expectations for a single mock must be defined in the same call. Mocking a
function in a inner providing
or verifying
call will override any
definitions made in the outer scope for the tests being run in the inner scope.
(testing "nested mocks"
(providing [(one-fn :argument-1) :result-1]
(providing [(one-fn :argument-2) :result-2
(other-fn :argument-3) :result-3]
(is (thrown? ExceptionInfo (one-fn :argument-1)))
(is (= :result-2 (one-fn :argument-2)))
(is (= :result-3 (other-fn :argument-3))))
(is (= :result-1 (one-fn :argument-1))))))
Can you improve this documentation? These fine people already did:
Pedro Matiello & Phillip MatesEdit on GitHub
cljdoc is a website building & hosting documentation for Clojure/Script libraries
× close