Reputation:
I am using http-kit as the server with wrap-json-body
from ring.middleware.json
to get the stringified JSON content sent from the client as the request body. My core.clj
is:
; core.clj
; ..
(defroutes app-routes
(POST "/sign" {body :body} (sign body)))
(def app (site #'app-routes))
(defn -main []
(-> app
(wrap-reload)
(wrap-json-body {:keywords? true :bigdecimals? true})
(run-server {:port 8080}))
(println "Server started."))
When I run the server using lein run
the method works correctly. I am stringifying the JSON and sending it from the client. The sign method gets the json correctly as {"abc": 1}
.
The problem is when during mock test. The sig
n method gets a ByteArrayInputStream
and I am using json/generate-string
to convert to string which fails in this case. I tried wrapping the handler in wrap-json-body
but it is not work. Here are my test cases I tried out core_test.clj
:
; core_test.clj
; ..
(deftest create-sign-test
(testing "POST sign"
(let [response
(wrap-json-body (core/app (mock/request :post "/sign" "{\"username\": \"jane\"}"))
{:keywords? true :bigdecimals? true})]
(is (= (:status response) 200))
(println response))))
(deftest create-sign-test1
(testing "POST sign1"
(let [response (core/app (mock/request :post "/sign" "{\"username\": \"jane\"}"))]
(is (= (:status response) 200))
(println response))))
(deftest create-sign-test2
(testing "POST sign2"
(let [response (core/app (-> (mock/body (mock/request :post "/sign")
(json/generate-string {:user 1}))
(mock/content-type "application/json")))]
(is (= (:status response) 200))
(println response))))
(deftest create-sign-test3
(testing "POST sign3"
(let [response
(wrap-json-body (core/app (mock/request :post "/sign" {:headers {"content-type" "application/json"}
:body "{\"foo\": \"bar\"}"}))
{:keywords? true :bigdecimals? true})]
(is (= (:status response) 200))
(println response))))
All of the fails with the following error:
Uncaught exception, not in assertion.
expected: nil
actual: com.fasterxml.jackson.core.JsonGenerationException: Cannot JSON encode object of class: class java.io.ByteArrayInputStream: java.io.ByteArrayInputStream@4db77402
How can I pass a JSON string as the body to the method in ring mock test?
Upvotes: 2
Views: 4058
Reputation: 13175
There are three issues in your code.
Your test doesn't wrap your app handler in wrap-json-body
so it might not get correctly parsed request body in your handler. You need to first wrap your app
in wrap-json-body
and then call it with your mock request. (You could also have your app
handler to be already wrapped instead of wrapping it both in your main function and tests)
(let [handler (-> app (wrap-json-body {:keywords? true :bigdecimals? true})]
(handler your-mock-request))
Your mock request doesn't include proper content type and your wrap-json-body
won't parse your request body to JSON. That's why your sign
function gets ByteArrayInputStream
instead of parsed JSON. You need to add content type to your mock request:
(let [request (-> (mock/request :post "/sign" "{\"username\": \"jane\"}")
(mock/content-type "application/json"))]
(handler request))
Verify that your sign
function returns a response map with JSON as string in body. If it creates response body as input stream you need to parse it in your test function. Below I am using cheshire
to parse it (converting JSON keys to keywords):
(cheshire.core/parse-stream (-> response :body clojure.java.io/reader) keyword)
Additionally instead of writing your JSON request body by hand you can use Cheshire to encode your data into JSON string:
(let [json-body (cheshire.core/generate-string {:username "jane"})]
...)
With those changes it should work correctly like in my slightly modified example:
(defroutes app-routes
(POST "/echo" {body :body}
{:status 200 :body body}))
(def app (site #'app-routes))
(let [handler (-> app (wrap-json-body {:keywords? true :bigdecimals? true}))
json-body (json/generate-string {:username "jane"})
request (-> (mock/request :post "/echo" json-body)
(mock/content-type "application/json"))
response (handler request)]
(is (= (:status response) 200))
(is (= (:body response) {:username "jane"})))
Upvotes: 2