A Clojure library for mocking clj-http requests.
This library is heavily influenced by clj-http-fake and borrows one or two functions from it. The main differences are:
- Mock routes are defined in a vector, not a map, and are searched in order.
- We use a different algorithm for matching routes, so avoid the combinatorial explosion of permutiations and cross-product.
- The routes themselves are defined as maps, and allow for very flexible matching.
Available from Clojars:
[clj-http-mock "0.1.0"]
(require '[clj-http.client :as http]
'[clj-http-mock.core :as mock])
(mock/with-mock-routes
[(mock/route :get "http://www.google.com/")
(constantly {:status 200 :body "Mocked"})]
(http/get "http://www.google.com/"))
;;=> {:status 200 :body "Mocked"}
To match any query parameters:
(mock/with-mock-routes
[(mock/route :get "http;//www.google.com/" mock/any)
(fn [{:keys [query-string]}]
{:status 200 :body query-string})]
(http/get "http://www.google.com?q=mock"))
;;=> {:status 200 :body "q=mock"}
Mock routes are defined as a vector. Requests are matched against the
routes in the order they are defined, and the first match wins. If no
mock route matches, the request is passed on to the original
clj-http.core/request
function unless *in-isolation*
is true, in
which case an exception is thrown. You can set in-isolation globally by
calling (mock/set-in-isolation! true)
or by using the helper macro
mock/with-mock-routes-in-isolation
to set up your mock routes.
A mock route is simply a Clojure map with the keys :method
, :scheme
,
:host
, :port
, :path
, and :query-params
. The values in the map
may be constants, regular expressons, sets, functions, maps, or nil, and
are matched against the corresponding field in the request as follows:
- nil matches
(nil? val)
- a function matches if
(f val)
returns a true value - a set matches if it contains
val
- a map matches if
val
is a map with the same keys and every value matches recursively according to these rules - a regular-expression matches if
val
is a matching string - anything else must match according to Clojure's
=
(I'm open to supporting bag=
for collections if that anyone needs
that.)
There is special handling of :port
to allow it to be unset in the mock
route or the request if it is the default port for the scheme. There is
also special handling of :path
, which treats ""
and /
as
equivalent.
For example, the following mock route:
{:method :get
:scheme :http
:host "www.google.com"
:port 80
:path "/"
:query-params {:q #".*"}}
would match all requests to http://www.google.com/?q=.*
. The
mock/route
function exists to make it easier to define routes. Called
with a single map argument, it will merge in some defaults, so the above
is equivalent to:
(mock/route {:host "www.google.com" :query-params {:q #".*"}})
Called with an HTTP method (:get, :put, :post, etc.) and URL, it will parse the URL to generate the route map for you. So we could also write:
(mock/route :get "http://www.google.com/")
to match GET requests with no query string, or:
(mock/route :get "http://www.google.com/" {:q #".*})
which will produce the map we started with. If you don't need special matching of the query parameters (values are constants that much match literally), you can define your route like so:
(mock/route :get "http://www.google.com/?q=foo&sort=relevance")
Note that, because we parse the query string and match against the
resulting map, this route will match both
http://www.google.com/?q=foo&sort=relevance
and
http://www.google.com/?sort=relevance&q=foo
.
The helper macros mock/with-mock-routes
and
mock/with-mock-routes-in-isolation
expect their first argument to be a
vector with an even number of elements (pairs of mock route and
handler); subsequent expressions are evaluated with the appropriate
bindings in place.
The clj-http-mock.response
namespace contains a few functions for
generating responses:
-
HTTP status 200, constant body
(ok-response "Body text")
-
HTTP status 200, body read from
clojure.java.io/resource
(resource-response "my-mocks/page.html")
-
HTTP status 302, Location header set to
url
(redirect-response url)
-
HTTP status 307, Location header set to
url
(redirect-response url :status 307)
-
HTTP status 404, empty body
(not-found-response)
Copyright © 2015 Ray Miller ray@1729.org.uk
Distributed under the Eclipse Public License either version 1.0 or (at your option) any later version.