Roberto Paredes
Roberto Paredes

Reputation: 307

Sending a mock ring request with session data

I'm trying to unit test a ring app with 'fake' session data using ring.mock.request. But looking at its documentation it doesn't seem to have the functionality.

In the real app, I'm using the ring.middleware.session wrapper:

(defroutes routes
  (GET "/transactions"  [{session :session}]
    (response (str "Hello " (:username session)))))

(def app
  (wrap-session routes))

And in my test:

(app (ring.mock.request/request :get "/transactions"))

But I'm kinda stuck on where to add this 'fake' session data for my request (e.g. something like (ring.mock.request/request :get "/transactions" :session {:username "foo"}).

Upvotes: 4

Views: 532

Answers (2)

Taylor Wood
Taylor Wood

Reputation: 16194

You can use peridot for this. The important feature:

peridot is designed to be used with ->, and maintains cookies across requests in the threading.

(-> (peri/session app)
    (peri/request "/session-setter") ;; this would be the route that *sets* the session value
    (peri/request "/transactions"))

Where /session-setter is a route I added just to set a value in your session:

(GET "/session-setter" []
  {:status 200
   :body "OK"
   :session {:username "Roberto"}})

Ultimately you should get a map like this (I've removed some keys for brevity):

{:response {:status 200,
            :headers {...},
            :body "Hello Roberto"},
 :request {...},
 :cookie-jar {"localhost" {"ring-session" {:value "04110b66-1281-4e88-9470-546911e21ca1",
                                           :path "/",
                                           :domain "localhost",
                                           :raw "ring-session=04110b66-1281-4e88-9470-546911e21ca1",
                                           :http-only true}}}}

Notice in the response, we now see :body "Hello Roberto" which was built using the session key :username.

Upvotes: 5

Ivan Grishaev
Ivan Grishaev

Reputation: 1681

There is no an option to provide the session data in that mocking library, because it depends on the server part, not the request by itself.

When the session middleware processes the request, it reads the cookie that represents session data signed with a secret key.

The idea is to prepare such a cookie and pass it within a request. But in such case, your test is not a black-box anymore, because you manipulate with some internal stuff that should not be touched and is subject to change.

One option would be to have a special handler available only when running tests that adds session data you need for further processing. In your test, you do two requests: the first one for that handler, and the second one to your normal handler but passing the cookies returned from the first request. That would be the most realistic case.

Another option would be to find a function inside ring.middleware.session guts that adds and signs session data to response. Or redefine some of them using with-redef-fn, what is almost the same.

Take a look at bare-session-request on the link above, the main logic runs in that function.

Upvotes: 2

Related Questions