如何使用环模拟请求模拟使用body作为JSON测试POST请求?

时间:2016-10-17 19:25:19

标签: unit-testing clojure mocking ring http-kit

我使用http-kit作为来自wrap-json-body的{​​{1}}的服务器,以获取从客户端发送的字符串化JSON内容作为请求正文。我的ring.middleware.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.")) 运行服务器时,该方法可以正常工作。我正在对JSON进行字符串化并从客户端发送它。 sign方法正确地将json作为lein run

问题是在模拟测试期间。 {"abc": 1} n方法获得sig,我使用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))))

如何将JSON字符串作为主体传递给环模拟测试中的方法?

1 个答案:

答案 0 :(得分:2)

您的代码中存在三个问题。

  1. 您的测试没有在wrap-json-body中包装您的应用处理程序,因此可能无法在您的处理程序中正确解析请求正文。您需要首先将app包裹在wrap-json-body中,然后使用模拟请求调用它。 (您也可以将app处理程序包装好,而不是将其包装在主函数和测试中)

    (let [handler (-> app (wrap-json-body {:keywords? true :bigdecimals? true})]
      (handler your-mock-request))
    
  2. 您的模拟请求未包含正确的内容类型,并且您的wrap-json-body不会将您的请求正文解析为JSON。这就是为什么您的sign函数获取ByteArrayInputStream而不是解析JSON的原因。您需要在模拟请求中添加内容类型:

    (let [request (-> (mock/request :post "/sign" "{\"username\": \"jane\"}")
                      (mock/content-type "application/json"))]
      (handler request))
    
  3. 验证您的sign函数返回一个响应映射,其中JSON为正文字符串。如果它创建响应体作为输入流,则需要在测试函数中对其进行解析。下面我使用cheshire来解析它(将JSON键转换为关键字):

    (cheshire.core/parse-stream (-> response :body clojure.java.io/reader) keyword)
    
  4. 此外,您可以使用Cheshire将数据编码为JSON字符串,而不是手动编写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"})))