【问题标题】:How to mock test POST requests with body as JSON using ring mock request?如何使用环模拟请求以 JSON 形式模拟测试 POST 请求?
【发布时间】:2016-10-17 19:25:19
【问题描述】:

我使用 http-kit 作为服务器,使用来自 ring.middleware.jsonwrap-json-body 来获取从客户端发送的字符串化 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}

问题是在模拟测试期间。 sign 方法得到一个 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 字符串作为主体传递给环模拟测试中的方法?

【问题讨论】:

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


    【解决方案1】:

    您的代码中有三个问题。

    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)
      

    另外,您可以使用 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"})))
    

    【讨论】:

    • 非常感谢! sign 方法正在正确获取 JSON。我已经尝试了好几个小时才能做到这一点。
    猜你喜欢
    • 1970-01-01
    • 2012-01-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-01-26
    相关资源
    最近更新 更多