如何使用 ring.mock.request 模拟 PUT 请求

How do I mock a PUT request using ring.mock.request

如何通过这个测试:

(ns imp-rest.parser-test-rest
  (:require [clojure.test :refer :all])
  (:require [ring.mock.request :as mock] )
  (:require [imp-rest.web :as w]))

(deftest test-parser-rest
  (testing "put settings"          
           (w/app 
             (mock/request :put "/settings/coordinateName" "FOO" ))

           (let [response (w/app (mock/request :get "/settings"))]
             (println response )
             (is (=  (get (:body response) :coordinateName) "FOO")))))

它失败了:

FAIL in (test-parser-rest) (parser_test_rest.clj:30)
put settings
expected: (= (get (:body response) :coordinateName) "FOO")
  actual: (not (= nil "FOO"))

这是我的处理程序:

(ns imp-rest.web
  (:use compojure.core)
  (:use ring.middleware.json-params)
  (:require [clj-json.core :as json])
  (:require [ring.util.response :as response])
  (:require [compojure.route :as route])
  (:require [imp-rest.settings :as s]))

(defn json-response [data & [status]]
   {:status (or status 200)
    :headers {"Content-Type" "application/json"}
    :body (json/generate-string data)}) 

(defroutes handler

   (GET "/settings" []
        (json-response (s/get-settings)))

   (GET "/settings/:id" [id]
        (json-response (s/get-setting id)))

   (PUT "/settings" [id value]
        (json-response (s/put-setting id value)))

   (route/not-found "Page not found") )

(def app
  (-> handler
    wrap-json-params))

公开这张地图(设置):

(ns imp-rest.settings)

(def settings 
  (atom 
    {:coordinateName nil
     :burnin nil
     :nslices nil
     :mrsd nil
     }))

(defn get-settings []
  @settings) 

(defn get-setting [id]
  (@settings (keyword id))) 

(defn put-setting [id value]
  (swap! settings assoc (keyword id) value)
  value) 

和入口点:

(ns imp-rest.core
  (:use ring.adapter.jetty)
  (:require [imp-rest.web :as web]))

(defn -main
  "Entry point"
  [& args]
  (do        
    (run-jetty #'web/app {:port 8080})
    );END;do
  );END: main

现在,当我 'lein run' 时,我可以提出这样的(有效)请求:

curl -X PUT -H "Content-Type: application/json" \
    -d '{"id" : "coordinateName", "value" : "FOO"}' \
    http://localhost:8080/settings

这就是我试图通过测试模拟的内容。任何帮助表示赞赏。

如果你想在你的 PUT /settings/:id 路由接受体中使用 {"value": "..."} 格式的 :id,你需要更改你的路由定义:

(defroutes handler

  (GET "/settings" []
    (json-response (s/get-settings)))

  (GET "/settings/:id" [id]
    (json-response (s/get-setting id)))

  (PUT "/settings/:id" [id value]
    (json-response (s/put-setting id value)))

  (route/not-found "Page not found"))

并更改您在测试中调用 PUT 端点的方式:

(w/app 
  (-> (mock/request
        :put
        "/settings/coordinateName"
        (json/generate-string {:value "FOO"}))
      (mock/content-type "application/json")))

改变了什么?

  1. :id 在您的 PUT URL 路由定义中 (/settings -> /settings/:id)
  2. 您的 PUT 请求没有发送正确的请求和内容类型。

如果你想要 PUT /settings 路由期望 {"id": "...", "value": "..."} 请求主体,那么你需要更改创建模拟请求的方式:

(w/app 
  (-> (mock/request
        :put
        "/settings"
        (json/generate-string {:id "coordinateName" :value "FOO"}))
      (mock/content-type "application/json"))

您的 curl 请求在 PUT 请求的正文中将参数指定为 JSON,但您的模拟请求尝试使用 URL 参数。

有两种解决方法:

  • compojure 可以自动转换参数,但前提是存在相关的中间件——您已将 wrap-json-params 添加到处理程序,但缺少 wrap-params。 Piotrek Bzdyl 的回答相当于在复合路线中明确这些参数。
  • 或者,您可以使用 request.mock.body.
  • 在模拟请求的正文中添加 ID/value 对作为 JSON