#unit-testing #clojure #mocking #ring #http-kit
#модульное тестирование #clojure #издевательство #ring #http-kit
Вопрос:
Я использую http-kit в качестве сервера с wrap-json-body
помощью from ring.middleware.json
, чтобы получить строковое содержимое JSON, отправленное от клиента в качестве тела запроса. Мой core.clj
:
; 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."))
Когда я запускаю сервер, используя lein run
метод, работает правильно. Я строю JSON и отправляю его от клиента. Метод sign правильно получает json как {"abc": 1}
.
Проблема в том, что во время макетного теста. Метод sig
n получает a ByteArrayInputStream
, и я использую json/generate-string
для преобразования в строку, которая в этом случае завершается с ошибкой. Я попытался обернуть обработчик, wrap-json-body
но это не сработало. Вот мои тестовые примеры, которые я опробовал 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))))
Все сбой со следующей ошибкой:
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
Как я могу передать строку JSON в качестве тела методу в ring mock test?
Ответ №1:
В вашем коде есть три проблемы.
-
Ваш тест не включает обработчик вашего приложения
wrap-json-body
, поэтому он может неправильно проанализировать тело запроса в вашем обработчике. Вам нужно сначала обернуть свойapp
inwrap-json-body
, а затем вызвать его с вашим макетным запросом. (Вы также моглиapp
бы уже обернуть свой обработчик вместо того, чтобы оборачивать его как в вашей основной функции, так и в тестах)(let [handler (-> app (wrap-json-body {:keywords? true :bigdecimals? true})] (handler your-mock-request))
-
Ваш макет запроса не включает правильный тип содержимого, и вы
wrap-json-body
не будете анализировать тело вашего запроса в JSON. Вот почему вашаsign
функция получаетByteArrayInputStream
вместо проанализированного JSON. Вам нужно добавить тип содержимого в свой макет запроса:(let [request (-> (mock/request :post "/sign" "{"username": "jane"}") (mock/content-type "application/json"))] (handler request))
-
Убедитесь, что ваша
sign
функция возвращает карту ответов с JSON в виде строки в теле. Если он создает тело ответа в качестве входного потока, вам необходимо проанализировать его в вашей тестовой функции. Ниже я используюcheshire
его для анализа (преобразование ключей JSON в ключевые слова):(cheshire.core/parse-stream (-> response :body clojure.java.io/reader) keyword)
Кроме того, вместо написания тела запроса JSON вручную вы можете использовать Cheshire для кодирования ваших данных в строку JSON:
(let [json-body (cheshire.core/generate-string {:username "jane"})]
...)
С этими изменениями он должен работать правильно, как в моем слегка измененном примере:
(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"})))
Комментарии:
1. Большое вам спасибо!
sign
Метод правильно получает JSON. Я часами пытался сделать это правильно.