This project has MOVED to Codeberg, a non-profit community forge:
https://codeberg.org/valpackett/clj-http-fake
https://codeberg.org/valpackett/clj-http-fake
Helper for faking clj-http requests | now on https://codeberg.org/valpackett/clj-http-fake
Home Page: https://codeberg.org/valpackett/clj-http-fake
This project has MOVED to Codeberg, a non-profit community forge:
https://codeberg.org/valpackett/clj-http-fake
https://codeberg.org/valpackett/clj-http-fake
Query params with spaces do not match the way they're supposed to.
Failing test here.
I think the best fix would be to stop doing string comparisons to match query strings, and instead parse requests as much as possible so we can do data comparisons and not worry about different ways of encoding things.
A short term fix could be something like this, but that might also break other things.
Would you consider a PR that added something like this?
Can you please update the version as pull request #32 is important to get our tests working with the newer clj-http client.
Hi, I'm trying to mock http calls from somewhere inside namespace (rather than explicitly like in the example). ie. My test goes...
(ns test.namespace
(:use my.namespace
clj-http.fake
clojure.test))
(deftest test-something
(is (= [] (this-function-calls-clj-http))))
Is this possible? Or do I need to use clj-http explicitly in the test to get the mocking?
I was thinking it would be really nice to have a couple of extra features in clj-http-fake. In particular, I'd like to be able to support different routes for different http verbs so that I could specify different handlers for GET vs. POST. My current idea is to have something like:
(with-fake-routes
[(get "http://example.com" (fn [req] ...))
(post "http://example.com" (fn [req] ...))
(any "http://example.com/resource" (fn [req ...))]
(some-body))
Also, the current behaviour when no route is matched is to fall through to the real http implementation. Whilst this might be desired in many cases, often in my tests I want to be isolate myself from the outside world completely. I'd quite like the ability to configure cclj-http-fake to throw instead of passing through to the real request handler...following on from the above, this could be achieved with something like:
(with-fake-routes
[(get "http://example.com" (fn [req] ...))
(default :throw)]
(some-body))
vs.
(with-fake-routes
[(get "http://example.com" (fn [req] ...))
(default :fall-through)]
(some-body))
or something along those lines.
What do you think? I'm happy to make the changes, I just wanted to get your opinion first.
Thanks,
Toby
Hello.
Later features such as :query-params
have not been released and are unavailable on Clojars. It's been a while since the last release, are you considering a new one soon?
First off, great library; it's made my life significantly easier. So I thought that I'd combine it with quick check.
I'm testing an API that takes at least 6 different parameters, sometimes more depending on the particular testing scenario, and I noticed that my test runtimes would increase dramatically, and to the point where they simply wouldn't terminate.
I eventually traced this back to the number of query parameters the API under test was producing. The long and short of it is, as I'm sure you're aware, that potential-query-strings-for
combined with the cartesian-product
function produce something like N! address-strings which were then being matched on, where N is the number of query parameters.
Here is a test to demonstrate the issue (it takes an extremely long time to complete):
(deftest matches-many-qparams
(let [num-qparams 10]
(is (= (with-fake-routes
{#"http://test/\?.*"
(fn [request]
{:status 200 :headers {} :body "29RQPV"})}
(:body (http/request
{:uri "http://test/"
:query-params (zipmap (map str (range 0 num-qparams))
(range 0 num-qparams))
:method :get})))
"29RQPV"))))
Also, since all the default implementations of RouteMatcher eventually delegate to the Pattern impl, they all suffer from this problem.
Of course, the workaround is to just supply my own implementation, but I feel like there's an opportunity to help my fellow users out.
I was curious if you would be open to a PR which:
address
and request
address
, but by eagerly producing an address-string
we may be able to match quickly. In a similar vein, making potential-query-strings-for
lazy may accomplish the same thingHi there,
I've been trying to use clj-http-fake to wrap POST requests with form data, using ring's wrap-params
middleware to parse the form. However this fails because the request body is being wrapped in a StringEntity
object which wrap-params
blows up on. Here is my failing test:
(ns clj-http-fake-demo.core-test
(:require [clojure.test :refer :all]
[ring.middleware.params :refer (wrap-params)]
[ring.util.response :refer (response status)]
[clj-http.fake :refer (with-fake-routes)]
[clj-http.client :as http]))
(deftest form-body-test
(let [fake-server (wrap-params
(fn [req]
(-> (response (get-in req [:form-params "foo"] "null"))
(status 201))))]
(with-fake-routes {#".*" fake-server}
(let [resp (http/post "http://example.com/" {:form-params {:foo "bar"}})]
(is (= (:status resp) 201))
(is (= (:body resp) "bar"))))))
The stacktrace I get starts like this:
actual: java.lang.IllegalArgumentException: Cannot open <#<StringEntity org.apache.http.entity.StringEntity@8864944>> as an InputStream.
at clojure.java.io/fn (io.clj:173)
clojure.java.io$fn__8577$G__8542__8584.invoke (io.clj:73)
clojure.java.io/fn (io.clj:169)
clojure.java.io$fn__8551$G__8546__8558.invoke (io.clj:73)
clojure.java.io$reader.doInvoke (io.clj:106)
clojure.lang.RestFn.applyTo (RestFn.java:139)
clojure.core$apply.invoke (core.clj:619)
clojure.core$slurp.doInvoke (core.clj:6278)
clojure.lang.RestFn.invoke (RestFn.java:439)
ring.middleware.params$assoc_form_params.invoke (params.clj:29)
ring.middleware.params$params_request.doInvoke (params.clj:39)
clojure.lang.RestFn.invoke (RestFn.java:423)
ring.middleware.params$wrap_params$fn__5209.invoke (params.clj:58)
clj_http.fake$try_intercept.invoke (fake.clj:116)
Which I believe is caused by a call to slurp
in ring.middleware.params.
This has been discussed on irc before, where the suggestion was made that you could wrap the app in a custom middleware to fix the body to unwrap the StringEntity. That led me to write this test, which passes as expected:
(defn wrap-fix-body [app]
(fn [req]
(letfn [(fix-body [body]
(if (isa? (class body) org.apache.http.entity.StringEntity)
(.getContent body)
body))]
(app (update-in req [:body] fix-body)))))
(deftest fixed-form-body-test
(let [fake-server (wrap-fix-body
(wrap-params
(fn [req]
(-> (response (get-in req [:form-params "foo"] "null"))
(status 201)))))]
(with-fake-routes {#".*" fake-server}
(let [resp (http/post "http://example.com/" {:form-params {:foo "bar"}})]
(is (= (:status resp) 201))
(is (= (:body resp) "bar"))))))
Is this something that clj-http-fake should be doing for the caller?
Hi!
Would be nice to add support for async requests from clj-http.
It requires some additions to the API that could be a bit challenging but still useful in asynchronous scenarios.
In my use-case it can improve things a lot in terms of tests backward-compatibility while moving certain parts of the application to clj-http/3.x and async requests.
Iโm trying to test some code that looks like this:
(c/get
"http://example.com"
{:async? true
:as :transit+msgpack
:accept :transit+msgpack}
(fn [r]
;; do something with (:body r), a map
)
(fn [e]
;; ...
))
It relies on clj-http
โs :as
key to parse the response as transit+msgpack
and pass the result to the response function. However, clj-http.fake
thinks the body is always a string:
clj-http.fake/handle-request-for-route fake.clj: 163
clj-http.fake/body-bytes fake.clj: 144
clj-http.fake/utf8-bytes fake.clj: 127
java.lang.ClassCastException: clojure.lang.PersistentVector cannot be cast to java.lang.String
clj-http.fake
should only do this bytes thing if the body is a string.
I can make a PR for this later.
If I evaluate following code
(with-fake-routes
{"http://google.com"
(fn [request]
{:status 200 :headers {} :body "R1BWm0"})}
(:body (http/get "http://google.cz/")))
I get following error:
1. Unhandled clojure.lang.ArityException
Wrong number of args (4) passed to: fake/try-intercept
AFn.java: 429 clojure.lang.AFn/throwArity
AFn.java: 44 clojure.lang.AFn/invoke
Var.java: 394 clojure.lang.Var/invoke
AFn.java: 165 clojure.lang.AFn/applyToHelper
Var.java: 700 clojure.lang.Var/applyTo
core.clj: 648 clojure.core/apply
core.clj: 641 clojure.core/apply
hooke.clj: 40 robert.hooke/compose-hooks/fn
RestFn.java: 137 clojure.lang.RestFn/applyTo
core.clj: 646 clojure.core/apply
core.clj: 641 clojure.core/apply
hooke.clj: 46 robert.hooke/run-hooks
hooke.clj: 45 robert.hooke/run-hooks
hooke.clj: 54 robert.hooke/prepare-for-hooks/fn/fn
RestFn.java: 137 clojure.lang.RestFn/applyTo
AFunction.java: 29 clojure.lang.AFunction$1/doInvoke
RestFn.java: 436 clojure.lang.RestFn/invoke
core.clj: 322 clj-http.core/request
core.clj: 321 clj-http.core/request
fake.clj: 173 clj-http.fake/try-intercept
fake.clj: 159 clj-http.fake/try-intercept
Var.java: 383 clojure.lang.Var/invoke
AFn.java: 156 clojure.lang.AFn/applyToHelper
Var.java: 700 clojure.lang.Var/applyTo
core.clj: 648 clojure.core/apply
core.clj: 641 clojure.core/apply
hooke.clj: 40 robert.hooke/compose-hooks/fn
RestFn.java: 137 clojure.lang.RestFn/applyTo
core.clj: 646 clojure.core/apply
core.clj: 641 clojure.core/apply
hooke.clj: 46 robert.hooke/run-hooks
hooke.clj: 45 robert.hooke/run-hooks
hooke.clj: 54 robert.hooke/prepare-for-hooks/fn/fn
RestFn.java: 137 clojure.lang.RestFn/applyTo
AFunction.java: 29 clojure.lang.AFunction$1/doInvoke
RestFn.java: 408 clojure.lang.RestFn/invoke
Var.java: 379 clojure.lang.Var/invoke
client.clj: 1013 clj-http.client/wrap-request-timing/fn
client.clj: 1046 clj-http.client/wrap-async-pooling/fn
headers.clj: 147 clj-http.headers/wrap-header-map/fn
client.clj: 773 clj-http.client/wrap-query-params/fn
client.clj: 796 clj-http.client/wrap-basic-auth/fn
client.clj: 813 clj-http.client/wrap-oauth/fn
client.clj: 833 clj-http.client/wrap-user-info/fn
client.clj: 965 clj-http.client/wrap-url/fn
client.clj: 344 clj-http.client/wrap-redirects/fn
client.clj: 397 clj-http.client/wrap-decompression/fn
client.clj: 593 clj-http.client/wrap-input-coercion/fn
client.clj: 648 clj-http.client/wrap-additional-header-parsing/fn
client.clj: 537 clj-http.client/wrap-output-coercion/fn
client.clj: 232 clj-http.client/wrap-exceptions/fn
client.clj: 691 clj-http.client/wrap-accept/fn
client.clj: 713 clj-http.client/wrap-accept-encoding/fn
client.clj: 674 clj-http.client/wrap-content-type/fn
client.clj: 915 clj-http.client/wrap-form-params/fn
client.clj: 950 clj-http.client/wrap-nested-params/fn
client.clj: 849 clj-http.client/wrap-method/fn
cookies.clj: 131 clj-http.cookies/wrap-cookies/fn
links.clj: 63 clj-http.links/wrap-links/fn
client.clj: 976 clj-http.client/wrap-unknown-host/fn
client.clj: 1149 clj-http.client/request*
client.clj: 1142 clj-http.client/request*
client.clj: 1155 clj-http.client/get
client.clj: 1151 clj-http.client/get
RestFn.java: 410 clojure.lang.RestFn/invoke
...
Compiler.java: 6927 clojure.lang.Compiler/eval
Compiler.java: 6890 clojure.lang.Compiler/eval
core.clj: 3105 clojure.core/eval
core.clj: 3101 clojure.core/eval
main.clj: 240 clojure.main/repl/read-eval-print/fn
main.clj: 240 clojure.main/repl/read-eval-print
main.clj: 258 clojure.main/repl/fn
main.clj: 258 clojure.main/repl
main.clj: 174 clojure.main/repl
RestFn.java: 1523 clojure.lang.RestFn/invoke
interruptible_eval.clj: 87 clojure.tools.nrepl.middleware.interruptible-eval/evaluate/fn
AFn.java: 152 clojure.lang.AFn/applyToHelper
AFn.java: 144 clojure.lang.AFn/applyTo
core.clj: 646 clojure.core/apply
core.clj: 1881 clojure.core/with-bindings*
core.clj: 1881 clojure.core/with-bindings*
RestFn.java: 425 clojure.lang.RestFn/invoke
interruptible_eval.clj: 85 clojure.tools.nrepl.middleware.interruptible-eval/evaluate
interruptible_eval.clj: 55 clojure.tools.nrepl.middleware.interruptible-eval/evaluate
interruptible_eval.clj: 222 clojure.tools.nrepl.middleware.interruptible-eval/interruptible-eval/fn/fn
interruptible_eval.clj: 190 clojure.tools.nrepl.middleware.interruptible-eval/run-next/fn
AFn.java: 22 clojure.lang.AFn/run
ThreadPoolExecutor.java: 1142 java.util.concurrent.ThreadPoolExecutor/runWorker
ThreadPoolExecutor.java: 617 java.util.concurrent.ThreadPoolExecutor$Worker/run
Thread.java: 745 java.lang.Thread/run
This error message is quite confusing. It would be great to know what's happening in this case.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.